//var newTreeJson = {id:0, item:[{id:1,text:"所有流程", "im0":"folderClosed.gif", "im1":"folderOpen.gif"}]};
//var newTreeJson = {id:0, item:[{id:1,text:"所有流程", "im0":"folderClosed.gif", "im1":"folderOpen.gif", item:[{id:"FLOW-11",text:"测试"},{id:"CATE-11",text:"测试分类",item:[{id:"FLOW-12",text:"测试"}]}]}]};
var newCanvasHtml = '<div id="bpmncanvas" class="canvas" style="width:100%;height:100%;"></div><div id="bpmn-drop-zone" style="display:none;"></div>';//用于生成包裹画布的div

var fl = {};    //全局变量，存放设计器需要的dom对象，方法等

fl.isloading = false;   //当前是否在加载流程图过程中

//fl.mytree = null;
//fl.tmenu = null;
fl.tbcavans = null; //按钮栏

fl.curflow = null;	//当前流程
fl.curComp = null;	//当前组件

fl.canvasheight = 60;//64;	//画板高度，这里是默认值，根据泳道数自动计算
fl.milestonelaneheight = 60;//64;	//里程碑栏高度
fl.milestoneheight = 30;	//里程碑高度
//fl.milestoneunits = 5;	//里程碑默认跨度
fl.milestonedefaultwidth = 160;	//里程碑默认宽度
fl.milestoneminwidth = 100;	//里程碑默认宽度
fl.milestonestartx = 60;	//里程碑开始x值
fl.milestonestarty = 30;//32;	//里程碑开始y值
fl.lanelabelwidth = 30;	//泳道label宽度
fl.lanegrouplabelwidth = 30; //泳道分组label宽度

fl.laneheight = 120;	//泳道高度，注意flowmain中均为120,最好不在此修改
fl.laneminheight = 100;	//泳道最小高度  参考flowmain LANE_MIN_HEIGHT = 100,LANE_MIN_WIDTH = 300

fl.propcell_width = 380;//属性窗口宽度
fl.propcell_expand = true;//属性窗口默认展开
fl.cellcomp_width = 145;//可用组件栏宽度

fl.milestone; //里程碑栏SVG组件
fl.accComp; //可用组件列表手风琴控件

//fl.curflow.otherConfig.timelineno; //时间线节点数
//fl.curflow.otherConfig.timelineunit; //时间线单位（天、小时）
//fl.curflow.otherConfig.canvaswidth = 800;	//流程图宽度，默认泳道宽度

fl.unSavedChange = false;	//是否有未保存的修改


var bi = {};	//引用bpmnio方法对象

//初始化辅助配置
function initOtherConfig() {
    fl.curflow.otherConfig = {};
    fl.curflow.otherConfig.timelineno = 20; //默认到期日期
    fl.curflow.otherConfig.canvaswidth = 800;	//默认画布宽度
    fl.curflow.otherConfig.timelineunit = "day";	//默认时间单位
    fl.curflow.otherConfig.theme = "default";	//默认主题
}

//init tools 给当前页面增前全屏功能
(function() {

    //init fullscreen
    var fullScreenApi = {
            supportsFullScreen: false,
            isFullScreen: function() { return false; },
            requestFullScreen: function() {},
            cancelFullScreen: function() {},
            fullScreenEventName: '',
            prefix: ''
        },
        browserPrefixes = 'webkit moz o ms khtml'.split(' ');

    // check for native support
    if (typeof document.cancelFullScreen != 'undefined') {
        fullScreenApi.supportsFullScreen = true;
    } else {
        // check for fullscreen support by vendor prefix
        for (var i = 0, il = browserPrefixes.length; i < il; i++ ) {
            fullScreenApi.prefix = browserPrefixes[i];

            if (typeof document[fullScreenApi.prefix + 'CancelFullScreen' ] != 'undefined' ) {
                fullScreenApi.supportsFullScreen = true;

                break;
            }
        }
    }

    // update methods to do something useful
    if (fullScreenApi.supportsFullScreen) {
        fullScreenApi.fullScreenEventName = fullScreenApi.prefix + 'fullscreenchange';

        fullScreenApi.isFullScreen = function() {
            switch (this.prefix) {
                case '':
                    return document.fullScreen;
                case 'webkit':
                    return document.webkitIsFullScreen;
                default:
                    return document[this.prefix + 'FullScreen'];
            }
        };
        fullScreenApi.requestFullScreen = function(el) {
            return (this.prefix === '') ? el.requestFullScreen() : el[this.prefix + 'RequestFullScreen']();
        };
        fullScreenApi.cancelFullScreen = function(el) {
            return (this.prefix === '') ? document.cancelFullScreen() : document[this.prefix + 'CancelFullScreen']();
        };
    }

    // jQuery plugin
    if (typeof(jQuery) != 'undefined') {
        jQuery.fn.requestFullScreen = function() {

            return this.each(function() {
                if (fullScreenApi.supportsFullScreen) {
                    fullScreenApi.requestFullScreen(this);
                }
            });
        };
    }

    // export api
    window.fullScreenApi = fullScreenApi;
})();



//init main layout 初始化页面布局
function initLayout() {

    //fl.lomain = new dhtmlXLayoutObject(document.body, '2U', dhx_skin);
	/*fl.lomain = new dhtmlXLayoutObject('maindiv', '2U', dhx_skin);

	 fl.lomain.cells('a').setText(fmd_i18n_flow);
	 fl.cell_designer.setText(fmd_i18n_flowdesign);

	 fl.lomain.attachEvent("onExpand", function(id){
	 if('a'==id && fl.locanvas && !fl.locanvas.cells('a').isCollapsed()) {
	 fl.locanvas.cells('a').setWidth(fl.cellcomp_width);
	 fl.loaccComp.setSizes();
	 }
	 });
	 fl.lomain.attachEvent("onCollapse", function(id){
	 if('a'==id && fl.locanvas && !fl.locanvas.cells('a').isCollapsed()) {
	 fl.locanvas.cells('a').setWidth(fl.cellcomp_width);
	 fl.loaccComp.setSizes();
	 }
	 });*/

    fl.lomain = new dhtmlXLayoutObject('maindiv', '1C', dhx_skin);

    fl.cell_designer = fl.lomain.cells('a');
    fl.cell_designer.setText(fmd_i18n_flowdesign);

    window.onresize = function() {
        fl.lomain.setSizes();
    };

    //流程定义树
    //initFlowTree();

    //初始化属性定义通用的元数据
    fpf_initComponentMeta();

    //加载组件属性定义
    fpf_loadCompModules();
}

//清空设计区
function cleanCavansLayout() {
    fmdc.grid_prop && fmdc.grid_prop.destructor && fmdc.grid_prop.destructor();
    fmdc.grid_prop=null;
    if (fl.locanvas) {
        fl.locanvas.unload();
        fl.locanvas = null;
    }
    //fl.loaccComp = null;
    //fl.accComp = null;
    fl.propCell = null;
    //fl.cell_prop_tb = null;
}

//设计器初始化
function refreshCavansLayout() {

    //清空器计区
    cleanCavansLayout();

    //新建layout
    if (!fl.locanvas) {
        var pattern1 = flvar.isEditor ? '3W' : '2U';
        fl.locanvas = fl.cell_designer.attachLayout(pattern1);
    }

    //新建按钮栏
    if (!fl.tbcavans) {
        fl.tbcavans = fl.cell_designer.attachToolbar();

        //设置按钮栏
        fl.tbcavans.setIconsPath(flvar.iconpath);
        //添加按钮 从右至左
        fl.tbcavans.addButton('btn_close', 0, fmd_i18n_b_close, 'browserClose1.png', 'browserClose1_dis.png');
        fl.tbcavans.addButton('btn_help', 0, fmd_i18n_b_help, 'help1.png', 'help1_dis.png');
        fl.tbcavans.addText('txt_curflow', 0, '');//用于显示当前流程名称
        fl.tbcavans.addSeparator('sp4', 0);//分割线

        fl.tbcavans.addButton('btn_downpdf', 0, '', 'downsvg.png', 'downsvg_dis.png');//导出PDF
        fl.tbcavans.addButton('btn_downsvg', 0, '', 'downsvg.png', 'downsvg_dis.png');//导出svg
        fl.tbcavans.addButton('btn_downbpmn', 0, '', 'downbpmn.png', 'downbpmn_dis.png');//导出bpmn
        fl.tbcavans.addSeparator('sp3', 0);

        //fl.tbcavans.addButton('btn_settimeline', 0, '', 'time1.png', 'time1_dis.png');

        fl.tbcavans.addButton('btn_resetscroll', 0, '', 'relocate1.png', 'relocate1_dis.png');//定位到原点
        fl.tbcavans.addButton('btn_fs1', 0, '', 'fullscreen1.png', 'fullscreen1_dis.png');//全屏
        fl.tbcavans.addButton('btn_fs', 0, '', 'fullscreen.png', 'fullscreen_dis.png');//全屏
        fl.tbcavans.addButton('btn_theme', 0, '', 'theme1.png', 'theme1_dis.png');//修改主题
        fl.tbcavans.addSeparator('sp2', 0);

        fl.tbcavans.addButton('btn_showValidate', 0, '', 'showValidation.png', 'showValidation_dis.png');//显示验证消息
        fl.tbcavans.addButton('btn_validate', 0, '', 'validation.png', 'validation_dis.png');//验证
        fl.tbcavans.addSeparator('sp1', 0);

        fl.tbcavans.addButton('btn_redo', 0, fmd_i18n_b_redo, 'redo.png', 'redo_dis.png');
        fl.tbcavans.addButton('btn_undo', 0, fmd_i18n_b_undo, 'undo.png', 'undo_dis.png');
        fl.tbcavans.addSeparator('sp0', 0);
        fl.tbcavans.addButton('btn_reset', 0, fmd_i18n_b_reset, 'revert.png', 'revert_dis.png');
        fl.tbcavans.addButton('btn_save', 0, fmd_i18n_b_save, 'save.png', 'save_dis.png');

        fl.tbcavans.setItemToolTip('btn_close', fmd_i18n_b_close);
        fl.tbcavans.setItemToolTip('btn_help', fmd_i18n_b_help);
        fl.tbcavans.setItemToolTip('btn_downpdf', fmd_i18n_b_downpdf);
        fl.tbcavans.setItemToolTip('btn_downsvg', fmd_i18n_b_downsvg);
        fl.tbcavans.setItemToolTip('btn_downbpmn', fmd_i18n_b_downbpmn);
        //fl.tbcavans.setItemToolTip('btn_settimeline', '时间单位设置');
        fl.tbcavans.setItemToolTip('btn_resetscroll', fmd_i18n_b_resetscroll);
        fl.tbcavans.setItemToolTip('btn_theme', fmd_i18n_b_theme);
        fl.tbcavans.setItemToolTip('btn_showValidate', fmd_i18n_b_showValidate);
        fl.tbcavans.setItemToolTip('btn_validate', fmd_i18n_b_validate);
        fl.tbcavans.setItemToolTip('btn_fs', fmd_i18n_b_fs);
        fl.tbcavans.setItemToolTip('btn_fs1', fmd_i18n_b_fs1);
        fl.tbcavans.setItemToolTip('btn_redo', fmd_i18n_b_redo);
        fl.tbcavans.setItemToolTip('btn_undo', fmd_i18n_b_undo);
        fl.tbcavans.setItemToolTip('btn_reset', fmd_i18n_b_reset);
        fl.tbcavans.setItemToolTip('btn_save', fmd_i18n_b_save);

        fl.tbcavans.addSpacer('txt_curflow');

        //按钮栏事件
        fl.tbcavans.attachEvent('onClick', function(btid) {
            if (btid=='btn_downbpmn') {//导出bpmn
                bi.saveDiagram(function(err, xml){
                    downloadFile(fl.curflow.name+".bpmn", xml);
                });
            } else if (btid=='btn_downsvg') {//导出svg
                bi.saveSVG(function(err, xml){
                    unSelect();
                    downloadFile(fl.curflow.name+".svg", xml);
                });
            } else if (btid=='btn_save') {//保存
                validateFlowAndSave(true);
            } else if (btid=='btn_validate') {//验证
                validateFlowAndSave(false);
            } else if (btid=='btn_showValidate') {//显示验证错误
                showValidationErrors(false);
            } else if (btid=='btn_reset') {//重置
                if (fl.unSavedChange) {
                    dhtmlx.message({
                        "type": "confirm-warning",
                        "title" : fmd_i18n_title_tip,
                        "text": fmd_i18n_msg_unsavedchange,
                        "ok" : fmd_i18n_b_yes,
                        "cancel" : fmd_i18n_b_no,
                        "callback" : function(y) {if (y) {editFlow();}}
                    });
                } else {
                    editFlow();
                }
            } else if (btid=='btn_undo') {//撤销
                fl_waitstart();
                bi.editorActions.undo();
                fl_waitend();
            } else if (btid=='btn_redo') {//重做
                fl_waitstart();
                bi.editorActions.redo();
                fl_waitend();
            } else if (btid=='btn_fs') {//全屏设计区
                fullScreenApi.requestFullScreen(document.querySelector('.canvas'));
            } else if (btid=='btn_fs1') {//全屏设计器
                //alert(document.querySelector('.djs-container').innerHTML);
                var el = $('#maindiv > .dhx_cell_layout:last')[0];
                if (fullScreenApi.isFullScreen(el)) {
                    fullScreenApi.cancelFullScreen(el);
                } else {
                    fullScreenApi.requestFullScreen(el);
                }
            } else if (btid=='btn_resetscroll') {//定位到原点
				/*bi.iocanvas.zoom('fit-viewport');//定位到原点
				 bi.iocanvas.zoom(1);//重置缩放比
				 var dy = 60;//y轴偏移量
				 if ($("svg g[data-element-id^='MileStoneGroup_']").length) {
				 dy = 0;//含有阶段时偏移量
				 } else if ($("svg g[data-element-id^='MileStone_']").length) {
				 dy = 30;//仅含有步骤时偏移量
				 }
				 bi.iocanvas.scroll({dx:0,dy:dy});//定位偏离fix*/
                fl_resetScroll();
            }/* else if (btid=='btn_settimeline') {
			 setTimeline();
			 }*/ else if (btid=='btn_theme') {//切换主题
                fl_selectTheme();
            } else if (btid=='btn_help') {//切换主题
                openWindowByUrl(new Date().getTime()+"", flvar.url_help);
            } else if (btid=='btn_close') {//关闭
                //如果是全屏，先关闭全屏
                var el = $('#maindiv > .dhx_cell_layout:last')[0];
                if (fullScreenApi.isFullScreen(el)) {
                    fullScreenApi.cancelFullScreen(el);
                } else {
                    if (fl.unSavedChange) {
                        dhtmlx.message({
                            "type": "confirm-warning",
                            "title" : fmd_i18n_title_tip,
                            "text": fmd_i18n_msg_unsavedchange,
                            "ok" : fmd_i18n_b_yes,
                            "cancel" : fmd_i18n_b_no,
                            "callback" : function(y) {if (y) {closeWin();}}
                        });
                    } else {
                        closeWin();
                    }
                }
            } else if (btid=='btn_downpdf') {
                fl_downloadpdf();
            }
        });
    }
    //隐藏按钮
    hide_tbcavans_btns();

    //画板
    var cellCanvas = fl.locanvas.cells(flvar.isEditor ? 'b' : 'a');
    cellCanvas.hideHeader();
    cellCanvas.attachHTMLString(newCanvasHtml);
    //状态栏
    fl.stb = cellCanvas.attachStatusBar();

    //属性面板
    fl.propCell = fl.locanvas.cells(flvar.isEditor ? 'c' : 'b');
    fl.propCell.setText(fmd_i18n_propsetting);
    fl.propCell.setWidth(fl.propcell_width);
    fl.propcell_expand || fl.propCell.collapse();

    //可用组件栏
    if (flvar.isEditor) {
        var cellComp = fl.locanvas.cells('a');
        cellComp.setText('<i>'+fmd_i18n_flowcomp+'</i>');
        cellComp.setWidth(fl.cellcomp_width);
        cellComp.attachHTMLString('<div id="comp_palette_basic" style="width:100%;height:100%;"></div>');
    }

    //窗口大小变化后，使组件栏自适应
    fl.locanvas.attachEvent("onResizeFinish", function(){
        flvar.isEditor && $("#comp_palette_basic").css("width","100%").css("height","100%");
        if (fl.propCell && !fl.propCell.isCollapsed()) {
            fl.propCell.setWidth(fl.propcell_width);
        }
    });

    fl.locanvas.attachEvent("onPanelResizeFinish", function(){
        flvar.isEditor && $("#comp_palette_basic").css("width","100%").css("height","100%");
    });

}

//获取长度为len的随机字符串
/*function getRandomId(pre, len) {
 len = len || 32;
 var $chars = 'ABCDEFGHJKMNPQRSTWXYZabcdefhijkmnprstwxyz2345678'; // 默认去掉了容易混淆的字符oOLl,9gq,Vv,Uu,I1
 var maxPos = $chars.length;
 var pwd = '';
 for (var i = 0; i < len; i++) {
 pwd += $chars.charAt(Math.floor(Math.random() * maxPos));
 }
 return pre+pwd;
 }*/

//载入流程定义到设计器
function editFlow(theme) {

    var ts = new Date();

    //override functions for pi change
    if ('function'==typeof(window.overrideFlowFunctions)) {
        window.overrideFlowFunctions();
    }

    function loadFlowData(ret) {
        fl_waitstart();
        fl.isloading = true;	//set loading flag

        if (ret && ret.bpmn) {//读取成功，如果已进行了设计，则加载
            //初始化一个页面变量保存当前流程
            fl.curflow = {id:flvar.flowid, name:ret.bpdName, bpmnConfig:ret.bpmnConfig, status:ret.status, category:ret.category};
            if (!fl.curflow.bpmnConfig.processSettings) {//初始化processSettings
                fl.curflow.bpmnConfig.processSettings = {};
            }
            //初始化otherConfig
            if (ret.otherConfig) {
                fl.curflow.otherConfig = ret.otherConfig;
            } else {
                initOtherConfig();
            }

            //load theme if selected
            theme && (fl.curflow.otherConfig.theme = theme);	//切换主题
            fl_loadThemeJs(function () {
                //load bpmn
                bi.openDiagram(ret.bpmn, function() {
                    //console.debug("Loading of bpmn ended.");
                    postLoaded();
                    fl.unSavedChange = theme!=undefined;	//如果是修改主题，则fl.unSavedChange=true
                });
            });//加载新主题的js

        } else {//读取成功，但未进行了设计，则创建空流程图
            fl.curflow = {id:flvar.flowid, name:ret.bpdName, bpmnConfig:{processSettings:{}}, status:"00", category:ret.category};
            initOtherConfig();

            //load theme
            theme && (fl.curflow.otherConfig.theme = theme);	//切换主题
            fl_loadThemeJs(function () {
                //create new
                bi.createNewDiagram(function() {
                    postLoaded();
                    fl.unSavedChange = false;
                });
            });
        }
    }

    //加载完成的操作
    function postLoaded() {
        //切换主题后，清空样式默认值以使用新主题的值
        if (theme) {
            for (var elid in fl.curflow.bpmnConfig) {
                //跳过自定义的
                if ("processSettings"!=elid && fl.curflow.bpmnConfig[elid]["displayStyle"]=="theme") {
                    for (var p in fl.curflow.bpmnConfig[elid]) {
                        if (!p.startsWith("displayStyle_")) continue;
                        delete fl.curflow.bpmnConfig[elid][p];
                    }
                }
            }
            //切换主题后，清除浮动栏以重建
            if (fl.floatheader) {
                fl.floatheader.remove();
                fl.floatheader = undefined;
            }
        }
        //处理浮动栏
        fldmeta_prop.func.MileStone.copyMileStone(true);
        fldmeta_prop.func.MileStoneGroup.copyMileStoneGroup(true);
        fldmeta_prop.func.ParticipantGroup.copyParticipantGroup(true);
        fldmeta_prop.func.Lane.copyLane(true);
        //显示按钮
        show_tbcavans_btns();
        fl.tbcavans.setItemText('txt_curflow', '<span class="curflowtxt"><span>'+fmd_i18n_curflow+'</span><b>&nbsp;&nbsp;[&nbsp;'+fl.curflow.name+'&nbsp;]</b></span>');//设置显示名称
        fl.curComp = null;
        fl.isloading = false;
        setTimeout("unSelect()", 1000);//清空属性栏
        if('function'==typeof(window.fl_afterFlowLoaded)) {
            window.fl_afterFlowLoaded();
        }
        fl_waitend();
        var te = new Date();
        console.debug("Loading flow ellapsed------------------"+(te.getTime() - ts.getTime())+" milliseconds.");
    }

    //get data then call callback
    if (!flvar.tenantCfg) {
        doGetAsyncJson(flvar.restCtx+'/tenant/cfg', function (ret) {
            flvar.tenantCfg = ret;
            getFlowData(loadFlowData);
        });
    } else {
        getFlowData(loadFlowData);
    }

}

function getFlowData(callback) {
    setByTenantCfg();
    getFlowDataByBpdIdVid(flvar.flowid, flvar.versionid, callback);
}

function getFlowDataByBpdIdVid(bpdId, versionid, callback) {
    if (versionid) {
        doGetAsyncJson(flvar.bpmeRest + "/bpdWithVersion/" + bpdId+ "/"+versionid+'?timezoneOffset='+new Date().getTimezoneOffset(),
            function(ret){
                if (!ret) {
                    callback(bpd);
                } else {
                    var bpd = ret.bpd;
                    var version = ret.version;
                    bpd.bpmn = version.bpmn;
                    bpd.bpmn1 = version.bpmn1;
                    bpd.bpmnConfig = version.bpmnConfig;
                    bpd.bpmnConfig1 = version.bpmnConfig1;
                    bpd.svg = version.svg;
                    bpd.svg1 = version.svg1;
                    bpd.otherConfig = version.otherConfig;
                    bpd.otherConfig1 = version.otherConfig1;
                    callback(bpd);
                }
            },
            function () {
                msgErr(fmd_i18n_msg_readfail);
            });
    } else {
        doGetAsyncJson(flvar.bpmeRest + "/"+flvar.flowRes+"/" + bpdId +'?timezoneOffset='+new Date().getTimezoneOffset(),
            function(ret){
                if (typeof(ret.bpmnConfig)=='string') {
                    ret.bpmnConfig = JSON.parse(ret.bpmnConfig);
                    ret.otherConfig = JSON.parse(ret.otherConfig);
                }
                callback(ret);
            },
            function () {
                msgErr(fmd_i18n_msg_readfail);
            }
        );
    }
}

//validate flow
//callSave - 是否提示调用保存
function validateFlowAndSave(callSave) {
    //fl.cell_designer.progressOn();
    //fl_waitstart("开始验证流程");
    //console.log("validateFlowAndSave");

    //准备验证数据
    function validationPrepare() {
        //console.log("validationPrepare");
        fl.curflow.otherConfig.validationData = {};    //用于验证的数据
        fl.curflow.otherConfig.validationData.subProcessNormalStarts = {}; //临时记录内嵌子流程普通开始节点
        //获取bpmn xml 转换为 json
        bi.saveDiagram(function(err, xml){
            //bpmn转换为json格式，后续验证时使用
            fl.curflow.otherConfig.validationData.diagramJson = xml2json.fromStr(xml);
        });

        //验证时用到的需要提前从其他系统读取的配置信息
        var vdPromises = [];
        for (var elid in fl.curflow.bpmnConfig) {
            if (elid=='processSettings') continue;
            var i18ntype = fl.curflow.bpmnConfig[elid].i18ntype;
            //console.debug(elid+"====="+i18ntype);
            if (!i18ntype) continue;	//label or other things without type
            if (!fldmeta_prop.control[i18ntype].validationDataPrepare) continue;
            if (!fl.curflow.otherConfig.validationData[elid]) {
                fl.curflow.otherConfig.validationData[elid] = {};
            }
            for (var i in fldmeta_prop.control[i18ntype].validationDataPrepare) {
                var promise = fldmeta_prop.control[i18ntype].validationDataPrepare[i](elid);
                if (promise) {
                    vdPromises.push(promise);
                }
            }
        }
        return vdPromises;//返回异步句柄组成的数组，用于异步调用链
    };

    //验证
    function waitValidation(){
        //console.log("waitValidation");
        var dtd = $.Deferred();//使本方法异步执行

        function doValidation() {
            //console.log("doValidation");
            //1 validate bpmn
            var seList = getIORegistedElems("StartEvent");
            var subStartCount = {};
            if (seList.length==0) {//必须含有开始节点
                fl_pushValidationErr(null, null, "error", fmd_i18n_msg_noStartEvent);
            } else if (seList.length>1) {//多个开始节点时判断主流程开始节点数不能是多个
                var mainStartCountNormal = 0;	//记录主流程开始节点数
                var mainStartCountMessage = 0;	//记录主流程触发开始节点数
                for (var i=0; i<seList.length; i++) {
                    var sel1 = seList[i];
                    var evd = fldmeta_prop.func.getEventDefinition(sel1);
                    var p1 = sel1.parent;
                    if (!p1) continue;
                    if (p1.type=="bpmn:Participant") {
                        if (!evd) {
                            mainStartCountNormal ++; //父节点为泳道视为主流程上的开始
                        } else if (evd && evd.indexOf("MessageEventDefinition")>=0) {//消息触发开始
                            mainStartCountMessage ++;
                        }
                    } else if (p1.type=="bpmn:SubProcess") {
                        subStartCount[p1.id] = subStartCount[p1.id] || {'normal':0,'message':0};
                        if (!evd) {
                            subStartCount[p1.id].normal ++;
                            fl.curflow.otherConfig.validationData.subProcessNormalStarts[p1.id] = seList[i].id;
                        } else if (evd && evd.indexOf("MessageEventDefinition")>=0) {//消息触发开始
                            subStartCount[p1.id].message ++;
                        }
                    }
                }
                //检测主流程
                if (mainStartCountNormal>1) {
                    fl_pushValidationErr(null, null, "error", fmd_i18n_msg_only1StartEvent);
                }
                /*if (mainStartCountMessage>1) {
                 fl_pushValidationErr(null, null, "error", fmd_i18n_msg_only1StartEventMessage);
                 }*/
            }
            //检测内嵌子流程
            var subps = getIORegistedElems("SubProcess", ["id"], function(ef){return (!ef.collapsed || ef.isExpanded)});
            for (var i in subps) {
                var s = subps[i]["id"];
                if (!subStartCount[s]) {
                    fl_pushValidationErr(s, null, "error", fmd_i18n_flc_typenames.SubProcess+"("+s+") ["+(fl.curflow.bpmnConfig[s]["i18nname-zh"])+"] : "+fmd_i18n_msg_noStartEventSub);
                } else if (subStartCount[s].normal!=1 || subStartCount[s].message>0) {
                    fl_pushValidationErr(s, null, "error", fmd_i18n_flc_typenames.SubProcess+"("+s+") ["+(fl.curflow.bpmnConfig[s]["i18nname-zh"])+"] : "+fmd_i18n_msg_only1StartEventSub);
                }
            }
            if (getIORegistedElems("EndEvent").length==0) {//必须含有结束节点
                fl_pushValidationErr(null, null, "error", fmd_i18n_msg_noEndEvent);
            }

            //2 validate process variables
            if ("function"==typeof(fldmeta_prop.control.Collaboration.properties.processvars.validatorFunc)) {
                var result = fldmeta_prop.control.Collaboration.properties.processvars.validatorFunc(null, fl.curflow.bpmnConfig.processSettings.variables);
                if (result && "string"==typeof(result)) {
                    fl_pushValidationErr(null, null, "error", result);
                } else if (result && "object"==typeof(result)) {
                    fl_pushValidationErr(null, null, result.type, result.msg);
                }
            }

            //3 validate lane milestone containment
            validateAndCalculateContainment();

            //4 validate all compenents
            var confToDel = [];
            for (var elid in fl.curflow.bpmnConfig) {//遍历所有组件
                if (elid=='processSettings') continue;
                if (!getIORegistedElem(elid)) {
                    confToDel.push(elid);
                    continue;
                }
                var i18ntype = fl.curflow.bpmnConfig[elid].i18ntype;
                //console.debug(elid+"====="+i18ntype);
                if (!i18ntype) continue;	//label or other things without type

                //错误消息前缀
                var prefix = fldmeta_prop.i18n_type_names[i18ntype] +
                    " ("+elid+") ["+(fl.curflow.bpmnConfig[elid]["i18nname-zh"]||fmd_i18n_prop_untitled)+"] ";
                //验证所有属性
                var confs = fldmeta_prop.control[i18ntype];
                if (confs) {
                    //需要验证的属性
                    var confNames;
                    if (fl.curflow.bpmnConfig[elid].validateProperties) {
                        var ns1 = fl.curflow.bpmnConfig[elid].validateProperties.split(",");
                        confNames = {};
                        for (var i=0; i<ns1.length;i++) {
                            confNames[ns1[i]] = true;
                        }
                    } else {
                        confNames = fl.curflow.bpmnConfig[elid];
                    }
                    //验证单个属性
                    for (var p in confNames) {
                        if (p=='validateProperties' || p.startsWith('tiprow_')) {
                            continue;
                        }
                        //获取单个属性定义
                        var pconf = null;
                        if (confs.properties && confs.properties[p]) {	//存在properties中
                            pconf = confs.properties[p];
                        } else if (((confs.properties && !confs.properties[p]) || !confs.properties)
                            && confs["includes-properties"]) {//不存在properties中
                            for (var c in confs["includes-properties"]) {
                                if (confs["includes-properties"][c][p]) {
                                    pconf = confs["includes-properties"][c][p];
                                    break;
                                }
                            }
                        }
                        //TODO styles validation
                        //如果既不在properties中也不在includes-properties中则检查子属性
                        if (!pconf) {
                            pconf = JSONSelect.match(".sub ."+p, confs).pop();   //子属性
                        }
                        if (!pconf) {
                            pconf = JSONSelect.match(".conditional-sub ."+p, confs).pop();   //有条件的子属性
                        }
                        //translate function to object
                        if (typeof(pconf)=="function") {
                            pconf = pconf();
                        }
                        //console.debug("doValidation="+elid+"="+p+"=====pconf======"+JSON.stringify(pconf));
                        //验证
                        if (pconf) {
                            var prefixProp = prefix + fmd_i18n_prop_prop +
                                " ["+('function'==typeof(pconf.name)?pconf.name(elid):pconf.name)+"] : ";

                            //调用Validator验证属性值，如果有validatorFunc则不使用validator
                            if (pconf.validatorFunc && 'function'==typeof(pconf.validatorFunc)) {
                                pconf.validatorFunc(elid, fl.curflow.bpmnConfig[elid][p], prefixProp);
                            } else if (pconf.validator && 'string'==typeof(pconf.validator)) {
                                var b = dhtmlxValidation.validateByFunc(pconf.validator, fl.curflow.bpmnConfig[elid][p]);
                                if (!b) {
                                    result = prefixProp + fmd_i18n_vld_failed + " " + fldmeta_prop.runtime_validators_i18n[pconf.validator];
                                    fl_pushValidationErr(elid, p, "error", result);
                                }
                            } else if (pconf.validator && 'object'==typeof(pconf.validator)) {
                                var b = dhtmlxValidation.validateByFunc(pconf.validator.validator, fl.curflow.bpmnConfig[elid][p]);
                                if (!b) {
                                    result = prefixProp + fmd_i18n_vld_failed + " " + fldmeta_prop.runtime_validators_i18n[pconf.validator.validator];
                                    fl_pushValidationErr(elid, p, pconf.validator.level, result);
                                }
                            }
                        } else {
                            //console.warn(elid+" property "+p+" not found in validation process.");
                        }
                    }
                }

                //验证链接线
                if (i18ntype.indexOf('Task')>=0) {//人工任务
                    var ins = fl_getIncomingTransitions(elid, fl.curflow.otherConfig.validationData.diagramJson);//来源路径
                    if (!ins || ins.length==0) {
                        //ve.rows.push({"id":ve.rows.length+1, "data":["error", prefix + ": " + fmd_i18n_msg_noIncomingFlow, elid]});
                        fl_pushValidationErr(elid, null, "error", prefix + ": " + fmd_i18n_msg_noIncomingFlow);
                    }
                    var outs = fl_getOutgoingTransitions(elid, fl.curflow.otherConfig.validationData.diagramJson);//目标路径
                    if (!outs || outs.length==0) {
                        //ve.rows.push({"id":ve.rows.length+1, "data":["error", prefix + ": " + fmd_i18n_msg_noOutgoingFlow, elid]});
                        fl_pushValidationErr(elid, null, "error", prefix + ": " + fmd_i18n_msg_noOutgoingFlow);
                    } else if (outs.length>1) {
                        if (i18ntype=='ReceiveTask' || i18ntype=='ServiceTask') {//no route setting
                            fl_pushValidationErr(elid, null, "error", prefix + ": " + fmd_i18n_msg_tooMuchOutgoingFlow);
                        }
                    }
                    fl_addOuts(elid, outs);
                } else if (i18ntype.indexOf('SubProcess')>=0) {//子流程
                    var ins = fl_getIncomingTransitions(elid, fl.curflow.otherConfig.validationData.diagramJson);//来源路径
                    if (!ins || ins.length==0) {
                        //ve.rows.push({"id":ve.rows.length+1, "data":["error", prefix + ": " + fmd_i18n_msg_noIncomingFlow, elid]});
                        fl_pushValidationErr(elid, null, "error", prefix + ": " + fmd_i18n_msg_noIncomingFlow);
                    }
                    var outs = fl_getOutgoingTransitions(elid, fl.curflow.otherConfig.validationData.diagramJson);//目标路径
                    if (!outs || outs.length==0) {
                        //ve.rows.push({"id":ve.rows.length+1, "data":["error", prefix + ": " + fmd_i18n_msg_noOutgoingFlow, elid]});
                        fl_pushValidationErr(elid, null, "error", prefix + ": " + fmd_i18n_msg_noOutgoingFlow);
                    } else if (outs.length>1) {
                        fl_pushValidationErr(elid, null, "error", prefix + ": " + fmd_i18n_msg_cannotfork);
                    }
                    fl_addOuts(elid, outs);
                } else if (i18ntype.indexOf('Gateway')>=0) {//网关
                    var ins = fl_getIncomingTransitions(elid, fl.curflow.otherConfig.validationData.diagramJson);
                    if (!ins || ins.length==0) {
                        fl_pushValidationErr(elid, null, "error", prefix + ": " + fmd_i18n_msg_noIncomingFlow);
                    } else if (ins.length > 1) {
                        if (i18ntype!='InclusiveGateway') {
                            fl_pushValidationErr(elid, null, "error", prefix + ": " + fmd_i18n_msg_gatewayCannotJoin);
                        }
                    }
                    var outs = fl_getOutgoingTransitions(elid, fl.curflow.otherConfig.validationData.diagramJson);
                    if (!outs || outs.length==0) {
                        fl_pushValidationErr(elid, null, "error", prefix + ": " + fmd_i18n_msg_noOutgoingFlow);
                    } else if (outs && outs.length==1) {
                        if (i18ntype!='InclusiveGateway') {
                            fl_pushValidationErr(elid, null, "error", prefix + ": " + fmd_i18n_msg_gatewayCannotOnly1Outgoing);
                        }
                    }
                    fl_addOuts(elid, outs);
                } else if (i18ntype=='StartEvent') {//开始事件
                    var outs = fl_getOutgoingTransitions(elid, fl.curflow.otherConfig.validationData.diagramJson);
                    if (!outs || outs.length==0) {
                        //ve.rows.push({"id":ve.rows.length+1, "data":["error", prefix + ": " + fmd_i18n_msg_noOutgoingFlow, elid]});
                        fl_pushValidationErr(elid, null, "error", prefix + ": " + fmd_i18n_msg_noOutgoingFlow);
                    } else if (outs.length>1) {
                        //ve.rows.push({"id":ve.rows.length+1, "data":["error", prefix + ": " + fmd_i18n_msg_tooMuchOutgoingFlow, elid]});
                        fl_pushValidationErr(elid, null, "error", prefix + ": " + fmd_i18n_msg_tooMuchOutgoingFlow);
                    }
                    fl_addOuts(elid, outs);
                } else if (i18ntype=='EndEvent') {//结束事件
                    var ins = fl_getIncomingTransitions(elid, fl.curflow.otherConfig.validationData.diagramJson);
                    if (!ins || ins.length==0) {
                        //ve.rows.push({"id":ve.rows.length+1, "data":["error", prefix + ": " + fmd_i18n_msg_noIncomingFlow, elid]});
                        fl_pushValidationErr(elid, null, "error", prefix + ": " + fmd_i18n_msg_noIncomingFlow);
                    }
                } else if (i18ntype.indexOf('Intermediate')>=0) {//中间事件
                    var ins = fl_getIncomingTransitions(elid, fl.curflow.otherConfig.validationData.diagramJson);
                    if (!ins || ins.length==0) {
                        fl_pushValidationErr(elid, null, "error", prefix + ": " + fmd_i18n_msg_noIncomingFlow);
                    } else if (ins.length>1) {
                        fl_pushValidationErr(elid, null, "error", prefix + ": " + fmd_i18n_msg_tooMuchIncomingFlow);
                    }
                    var outs = fl_getOutgoingTransitions(elid, fl.curflow.otherConfig.validationData.diagramJson);
                    if (!outs || outs.length==0) {
                        fl_pushValidationErr(elid, null, "error", prefix + ": " + fmd_i18n_msg_noOutgoingFlow);
                    } else if (outs.length>1) {
                        fl_pushValidationErr(elid, null, "error", prefix + ": " + fmd_i18n_msg_tooMuchOutgoingFlow);
                    }
                    fl_addOuts(elid, outs);
                }

            }
            //delete conf for removed elems
            for (var i in confToDel) {
                delete fl.curflow.bpmnConfig[confToDel[i]];
            }

            //5 clear prepared validation data
            fl.curflow.otherConfig.validationData = null;
            dtd.resolve();
        }

        setTimeout(doValidation(), 1);//make it async
        return dtd.promise();
    };

    //开始验证
    fl_waitstart(fmd_i18n_msg_validating);
    //数据准备
    fl.curflow.otherConfig.validateErrors = {"rows":[]};   //验证消息
    /*var vdPromises = validationPrepare();
    //异步命令链 组成一个jQuery异步调用命令string，使用eval调用
    var promiseCmds = "$.when(fl_waitmsg('"+fmd_i18n_msg_validating+"')";
    if (vdPromises && vdPromises.length) {
        for (var i in vdPromises) {
            promiseCmds += ",vdPromises["+i+"]";
        }
    }
    promiseCmds += ").done(waitValidation)"+
        ".done(function(){return callShowValidationInfo("+(callSave?"true":"false")+")})"+ //显示验证信息
        ".done(fl_waitend)";
    console.log(promiseCmds);
    //执行
    eval(promiseCmds);*/
    doneValidation(validationPrepare, waitValidation, callSave);

}

//验证和计算 步骤阶段，席位分组，席位，组件之间相互包含关系
function validateAndCalculateContainment() {
    var pgs = getIORegistedElems("ParticipantGroup", ["id","x","y","width","height"]);
    var lns = getIORegistedElems("Lane", ["id","x","y","width","height"]);
    var mgs = getIORegistedElems("MileStoneGroup", ["id","x","y","width","height"]);
    var mss = getIORegistedElems("MileStone", ["id","x","y","width","height"]);
    pgs.sort(function(a,b) {return a.y - b.y;});
    lns.sort(function(a,b) {return a.y - b.y;});
    mgs.sort(function(a,b) {return a.x - b.x;});
    mss.sort(function(a,b) {return a.x - b.x;});

    var lnsSps = [], lnsEps = [], mssSps = [], mssEps = [];	//start and end points
    if (lns && lns.length) {
        for (var i in lns) {
            lnsSps.push(lns[i].y);
            lnsEps.push(lns[i].height + lns[i].y);
        }
    }
    if (mss && mss.length) {
        for (var i in mss) {
            mssSps.push(mss[i].x);
            mssEps.push(mss[i].width + mss[i].x);
        }
    }
    //验证席位分组是否与某个席位对齐
    fl.curflow.bpmnConfig["Participant_1"] = fl.curflow.bpmnConfig["Participant_1"] || {};
    fl.curflow.bpmnConfig["Participant_1"].layout = {"childParticipantGroups":[], "childLanes":[], "childMileStoneGroups":[], "childMileStones":[]};
    if (pgs && pgs.length) {
        for (var i in pgs) {
            if (lnsSps.indexOf(pgs[i].y)<0) {//开始节点对齐
                fl_pushValidationErr(pgs[i].id, null, "warning", fmd_i18n_msg_pgstartnotaligntolane + ": " + (fl.curflow.bpmnConfig[pgs[i].id]["i18nname-zh"] || fmd_i18n_untitled));
            }
            if (lnsEps.indexOf(pgs[i].height + pgs[i].y)<0) {//结束节点对齐
                fl_pushValidationErr(pgs[i].id, null, "warning", fmd_i18n_msg_pgendnotaligntolane + ": " + (fl.curflow.bpmnConfig[pgs[i].id]["i18nname-zh"] || fmd_i18n_untitled));
            }
            //记录分组顺序
            fl.curflow.bpmnConfig[pgs[i].id].layout = {"order" : Number(i)};
            //记录分组
            fl.curflow.bpmnConfig["Participant_1"].layout.childParticipantGroups.push(pgs[i].id);
        }
    }
    //验证阶段是否与某个步骤对齐
    if (mgs && mgs.length) {
        for (var i in mgs) {
            if (mssSps.indexOf(mgs[i].x)<0) {//开始节点对齐
                fl_pushValidationErr(mgs[i].id, null, "warning", fmd_i18n_msg_mgstartnotaligntoms + ": " + (fl.curflow.bpmnConfig[mgs[i].id]["i18nname-zh"] || fmd_i18n_untitled));
            }
            if (mssEps.indexOf(mgs[i].width + mgs[i].x)<0) {//结束节点对齐
                fl_pushValidationErr(mgs[i].id, null, "warning", fmd_i18n_msg_mgendnotaligntoms + ": " + (fl.curflow.bpmnConfig[mgs[i].id]["i18nname-zh"] || fmd_i18n_untitled));
            }
            //记录阶段顺序
            fl.curflow.bpmnConfig[mgs[i].id].layout = {"order" : Number(i)};
            //记录阶段
            fl.curflow.bpmnConfig["Participant_1"].layout.childMileStoneGroups.push(mgs[i].id);
        }
    }
    //记录泳道顺序和父对象
    for (var i in lns) {
        fl.curflow.bpmnConfig[lns[i].id] = fl.curflow.bpmnConfig[lns[i].id] || {};
        fl.curflow.bpmnConfig[lns[i].id].layout = {"order" : Number(i)};
        fl.curflow.bpmnConfig["Participant_1"].layout.childLanes.push(lns[i].id);
        var center = {x : Math.round(lns[i].x+ lns[i].width/2), y : Math.round(lns[i].y+ lns[i].height/2)};
        for (var j=0; j<pgs.length; j++) {
            if (pgs[j].y<center.y && center.y<=(pgs[j].y+pgs[j].height)) {
                fl.curflow.bpmnConfig[pgs[j].id].layout.childElements = fl.curflow.bpmnConfig[pgs[j].id].layout.childElements || [];
                fl.curflow.bpmnConfig[pgs[j].id].layout.childElements.indexOf(lns[i].id)<0 && fl.curflow.bpmnConfig[pgs[j].id].layout.childElements.push(lns[i].id);
                fl.curflow.bpmnConfig[lns[i].id].layout.parentGroup = pgs[j].id;
            }
        }
    }
    //记录步骤顺序和父对象
    for (var i in mss) {
        fl.curflow.bpmnConfig[mss[i].id] = fl.curflow.bpmnConfig[mss[i].id] || {};
        fl.curflow.bpmnConfig[mss[i].id].layout = {"order" : Number(i)};
        fl.curflow.bpmnConfig["Participant_1"].layout.childMileStones.push(mss[i].id);
        var center = {x : Math.round(mss[i].x+ mss[i].width/2), y : Math.round(mss[i].y+ mss[i].height/2)};
        for (var j=0; j<mgs.length; j++) {
            if (mgs[j].x<center.x && center.x<=(mgs[j].x+mgs[j].width)) {
                fl.curflow.bpmnConfig[mgs[j].id].layout.childElements || (fl.curflow.bpmnConfig[mgs[j].id].layout.childElements = []);
                fl.curflow.bpmnConfig[mgs[j].id].layout.childElements.indexOf(mss[i].id)<0 && fl.curflow.bpmnConfig[mgs[j].id].layout.childElements.push(mss[i].id);
                fl.curflow.bpmnConfig[mss[i].id].layout.parentGroup = mgs[j].id;
            }
        }
    }
    //计算泳道 步骤和组件包含关系
    for (var i in bi.iocanvas._elementRegistry._elements) {
        var el1 = bi.iocanvas._elementRegistry._elements[i].element;
        var type=getBpmnType(el1);
        if(!type) continue;
        var center = {x : Math.round(el1.x+ el1.width/2), y : Math.round(el1.y+ el1.height/2)};
        if (type=="StartEvent") {
            fl.curflow.bpmnConfig[el1.id] = fl.curflow.bpmnConfig[el1.id] || {};
            if (el1.outgoing && el1.outgoing.length==1) {
                fl.curflow.bpmnConfig[el1.id].layout = {"targetRef":el1.outgoing[0].target.id};
            }
            var evd = fldmeta_prop.func.getEventDefinition(el1);
            if (evd) {
                //事件开始点
                fl.curflow.bpmnConfig["Participant_1"].layout.eventStarts = fl.curflow.bpmnConfig["Participant_1"].layout.eventStarts || {};
                var evdObj = {"eventDef":evd};
                if (evd.indexOf("MessageEventDefinition")>=0) {//消息触发开始
                    evdObj.messageName=fl.curflow.bpmnConfig[el1.id].messageName;
                }
                fl.curflow.bpmnConfig["Participant_1"].layout.eventStarts[el1.id] = evdObj;
            } else {
                //记录唯一起普通开始点
                if (el1.parent.type=="bpmn:Participant") {
                    fl.curflow.bpmnConfig["Participant_1"].layout.startEvent = el1.id;
                }
            }
        } else if (type=="SequenceFlow") {
            fl.curflow.bpmnConfig[el1.id] = fl.curflow.bpmnConfig[el1.id] || {};
            fl.curflow.bpmnConfig[el1.id].layout = {"sourceRef":el1.source.id, "targetRef":el1.target.id};
        } else if (type.indexOf("Task")>=0 || type=="SubProcess" || type.indexOf("Event")>=0 || type.indexOf("Gateway")>=0) {
            fl.curflow.bpmnConfig[el1.id] = fl.curflow.bpmnConfig[el1.id] || {};
            fl.curflow.bpmnConfig[el1.id].layout = {};
            // if (el1.isExpanded===true || el1.collapsed===false) {
            //     continue; //内嵌子流程跳过
            // }
            for (var j=0; j<lns.length; j++) {
                if (lnsSps[j]<center.y && center.y<=lnsEps[j]) {
                    fl.curflow.bpmnConfig[lns[j].id].layout = fl.curflow.bpmnConfig[lns[j].id].layout || {};
                    fl.curflow.bpmnConfig[lns[j].id].layout.childElements || (fl.curflow.bpmnConfig[lns[j].id].layout.childElements = []);
                    fl.curflow.bpmnConfig[lns[j].id].layout.childElements.indexOf(el1.id)<0 && fl.curflow.bpmnConfig[lns[j].id].layout.childElements.push(el1.id);
                    fl.curflow.bpmnConfig[el1.id].layout.parentLane = lns[j].id;
                }
            }
            for (var j=0; j<mss.length; j++) {
                if (mssSps[j]<center.x && center.x<=mssEps[j]) {
                    fl.curflow.bpmnConfig[mss[j].id].layout = fl.curflow.bpmnConfig[mss[j].id].layout || {};
                    fl.curflow.bpmnConfig[mss[j].id].layout.childElements || (fl.curflow.bpmnConfig[mss[j].id].layout.childElements = []);
                    fl.curflow.bpmnConfig[mss[j].id].layout.childElements.indexOf(el1.id)<0 && fl.curflow.bpmnConfig[mss[j].id].layout.childElements.push(el1.id);
                    fl.curflow.bpmnConfig[el1.id].layout.parentMileStone = mss[j].id;
                }
            }
            //if it's in SubProcess
            if (getBpmnType(el1.parent)=="SubProcess") {
                fl.curflow.bpmnConfig[el1.id].layout.parentSubProcess = el1.parent.id;
            }
            if (type=="SubProcess" && el1.children && el1.children.length>0) {
                if (el1.isExpanded===true || el1.collapsed===false) {//内嵌子流程记录普通开始
                    fl.curflow.bpmnConfig[el1.id].layout.startEvent = fl.curflow.otherConfig.validationData.subProcessNormalStarts[el1.id];
                }
                fl.curflow.bpmnConfig[el1.id].layout.childElements = [];
                for (var j in el1.children) {
                    var tc = getBpmnType(el1.children[j]);
                    if (tc) {
                        fl.curflow.bpmnConfig[el1.id].layout.childElements.push(el1.children[j].id);
                    }
                }
            }
        }
    }
}

//add outgoing to elements
function fl_addOuts(elid, outs) {
    if (outs && outs.length>0) {
        fl.curflow.bpmnConfig[elid].layout = fl.curflow.bpmnConfig[elid].layout || {};
        var outgoings = {};
        for (var i in outs) {
            outgoings[outs[i].id] = outs[i].targetRef;
        }
        fl.curflow.bpmnConfig[elid].layout.outgoings = outgoings;
    }
}

//加载完成之后执行
function fl_postLoaded() {
}

//验证之后弹出提示保存
//callSave 是否提示调用保存
function callShowValidationInfo(callSave) {

    //console.log("callShowValidationInfo");

    if (fl.curflow.otherConfig.validateFailed) {//验证失败直接返回
        return;
    }

    //fl.cell_designer.progressOn();
    var dtd = $.Deferred();

    function showSave() {
        if (fl.curflow.otherConfig.validateErrors
            && fl.curflow.otherConfig.validateErrors.rows
            && fl.curflow.otherConfig.validateErrors.rows.length) {//如果有验证错误消息，弹出提示
            showValidationErrors(callSave);
        } else {//如果没有验证错误消息，直接检查是否需要保存
            if (callSave) {
                saveFlow();
            } else {
                msg(fmd_i18n_msg_noValidationError);
            }
        }
        dtd.resolve();
    }

    setTimeout(showSave(), 1);//make it async
    return dtd.promise();
}

//show validation errors 弹出提示验证错误消息
//callSave - 是否提示保存
function showValidationErrors(callSave) {
    if (fl.curflow.otherConfig.validateErrors
        && fl.curflow.otherConfig.validateErrors.rows
        && fl.curflow.otherConfig.validateErrors.rows.length) {
        //弹出窗口
        var w1 = fm_createCenterWindow('wid_ve', '<b>'+(callSave ? fmd_i18n_msg_hasValidateError : fmd_i18n_prop_valiErr)+'</b>', 800, 600);
        var lo1 = callSave ? w1.attachLayout("2E") : w1.attachLayout("1C");
        lo1.cells('a').hideHeader();
        if (callSave) {
            lo1.cells('b').hideHeader();
            lo1.cells('b').setHeight(50);
        }
        //lo1.cells("a").setText(fmd_i18n_msg_hasValidateError);
        //创建表格
        var gdve = lo1.cells('a').attachGrid();
        gdve.setHeader([fmd_i18n_prop_valiLvl,fmd_i18n_prop_valiErr,"ELEMID"],null,["text-align:center;","text-align:center;","text-align:center;"]);
        gdve.setIconPath(fldmeta_prop.gridconf.treeIconPath);
        gdve.setInitWidths("90,*,80");
        gdve.setColTypes("ro,rotxt,ro");
        gdve.setColSorting("str,str,str");
        gdve.setColAlign("center,left,left");
        gdve.setColAlign("center,left,left");
        gdve.setColumnsVisibility("false,false,true");
        gdve.setEditable(false);
        gdve.setLang(flvar.lang);
        //gdve.setColumnColor("#ffd2d2");
        gdve.init();
        //取消选择
        gdve.attachEvent("onRowSelect", function(rid,ind){gdve.clearSelection();});
        //绑定双击事件，如果错误是在某个组件上，则双击后直接定位并选择该组件
        gdve.attachEvent("onRowDblClicked", function(rid,ind){
            var elemid = gdve.cells(rid,2).getValue();
            if (elemid) {
                w1.close();
                selectIoElem(elemid);
            }
        });
        //gdve.parse(fl.curflow.otherConfig.validateErrors, 'json');
        var b1 = false, b2 = false;
        for (var i in fl.curflow.otherConfig.validateErrors.rows) {
            gdve.addRow(fl.curflow.otherConfig.validateErrors.rows[i].id, [fmd_i18n_prop_validatetype_enum[fl.curflow.otherConfig.validateErrors.rows[i].data[0]], fl.curflow.otherConfig.validateErrors.rows[i].data[1], fl.curflow.otherConfig.validateErrors.rows[i].data[2]]);
            if (fl.curflow.otherConfig.validateErrors.rows[i].data[0]=='error') {
                gdve.setRowColor(fl.curflow.otherConfig.validateErrors.rows[i].id, "#ffd2d2");
                b1 = true;//有错误
            } else {
                gdve.setRowColor(fl.curflow.otherConfig.validateErrors.rows[i].id, "#fbffb4");
                b2 = true;//有警告
            }
        }
        //如果既有错误也有警告，则进行分组和排序，错误在前警告在后
        b1 && b2 && gdve.sortRows(0, "str", "des") && gdve.groupBy(0);
        if (callSave) {//是否提示保存
            var str2 = [{ type:"block" , name:"form_block_1", offsetLeft:(dhx_skin=="dhx_web"?"297":"302"), offsetTop:"0",
                list:[{ type:"button" , name:"fb_yes", value:"&nbsp;&nbsp;"+fmd_i18n_b_save+"&nbsp;&nbsp;", inputTop:0},{ type:"newcolumn"},{ type:"button", name:"fb_no", value:"&nbsp;&nbsp;"+fmd_i18n_b_cancel+"&nbsp;&nbsp;", inputTop:0  }] }];
            var fm2 = lo1.cells('b').attachForm(str2);
            fm2.attachEvent("onButtonClick", function(btid) {
                if (btid=='fb_yes') {
                    w1.close();
                    saveFlow();
                } else if (btid=='fb_no') {
                    w1.close();
                }
            });
        }
        //启用验证消息按钮
        fl.tbcavans.enableItem('btn_showValidate');
    } else {
        msg(fmd_i18n_msg_noValidationError);
    }
}

//保存
function saveFlow() {
    bi.saveDiagram(function(err, xml){
        unSelect();//取消选择
        //组装参数
        var param = {};
        param.bpdId = fl.curflow.id;
        param.bpmn = xml;
        bi.saveSVG(function(err, xml){
            param.svg = xml;
        });
        param.bpmnConfig = fl.curflow.bpmnConfig;
        param.otherConfig = fl.curflow.otherConfig;
        param.otherConfig.canvasheight = fl.canvasheight;
        var ret = doPostSyncJson(flvar.bpmeRest + "/"+flvar.flowRes+"/saveModel",
            {"bpd" : JSON.stringify(param)});
        if (!ret || !ret.success) {
            msgErr(fmd_i18n_msg_commitfail);
            fl.cell_designer.progressOff();
        } else {
            fl.unSavedChange = false;
            msg(fmd_i18n_success);
            fl.cell_designer.progressOff();
        }
    });
}

/**
 * 增加新的验证信息
 * elemid - 组件id
 * prop - 组件属性
 * type - 类型 warning-警告 error-错误
 * msg - 消息
 */
function fl_pushValidationErr(elemid, prop, type, msg) {
    fl.curflow.otherConfig.validateErrors.rows.push({"id":fl.curflow.otherConfig.validateErrors.rows.length+1, "data":[type, msg, elemid, prop]});
}

//判断是否有验证错误，不是警告
function fl_hasValidationErrors() {
    var ve = fl.curflow.otherConfig.validateErrors;
    if (ve && ve.rows && ve.rows.length) {
        for (var i in ve.rows) {
            if ('error'==ve.rows[i].data[0]) {
                return true;
            }
        }
    }
    return false;
}

/*function changeIco(item) {
 if (item.id.indexOf("CATE-")!=-1) item.im0 = "folderClosed.gif";
 if (item.item && item.item.length>0) {
 for (var i=0; i<item.item.length; i++) {
 //alert(item.text);
 changeIco(item.item[i]);
 }
 }
 }*/

//显示按钮栏按钮
function show_tbcavans_btns() {
    if (!fl.tbcavans) return;
    fl.tbcavans.showItem('sp0');
    //fl.tbcavans.showItem('sp1');
    //fl.tbcavans.showItem('sp2');
    fl.tbcavans.showItem('sp3');
    flvar.isEditor && fl.tbcavans.showItem('sp4');
    flvar.isEditor && fl.tbcavans.showItem('btn_save');
    flvar.isEditor && fl.tbcavans.showItem('btn_reset');
    //fl.tbcavans.showItem('btn_redo');
    //fl.tbcavans.showItem('btn_undo');
    //fl.tbcavans.showItem('btn_downbpmn');
    fl.tbcavans.showItem('btn_downsvg');
    //fl.tbcavans.showItem('btn_downpdf');
    //fl.tbcavans.showItem('btn_validate');
    fl.tbcavans.showItem('btn_showValidate');
    flvar.isEditor && fl.tbcavans.showItem('btn_theme');
    fl.tbcavans.showItem('btn_fs');
    fl.tbcavans.showItem('btn_fs1');
    fl.tbcavans.showItem('btn_resetscroll');
    //fl.tbcavans.showItem('btn_settimeline');
}

//隐藏按钮栏按钮
function hide_tbcavans_btns() {
    if (!fl.tbcavans) return;
    fl.tbcavans.hideItem('sp0');
    fl.tbcavans.hideItem('sp1');
    fl.tbcavans.hideItem('sp2');
    fl.tbcavans.hideItem('sp3');
    fl.tbcavans.hideItem('sp4');
    fl.tbcavans.hideItem('btn_save');
    fl.tbcavans.hideItem('btn_reset');
    fl.tbcavans.hideItem('btn_redo');
    fl.tbcavans.hideItem('btn_undo');
    fl.tbcavans.hideItem('btn_downbpmn');
    fl.tbcavans.hideItem('btn_downsvg');
    fl.tbcavans.hideItem('btn_downpdf');
    fl.tbcavans.hideItem('btn_validate');
    fl.tbcavans.hideItem('btn_showValidate');
    fl.tbcavans.hideItem('btn_theme');
    fl.tbcavans.hideItem('btn_fs');
    fl.tbcavans.hideItem('btn_fs1');
    fl.tbcavans.hideItem('btn_resetscroll');
    //fl.tbcavans.hideItem('btn_settimeline');
}

//导出bpmn或svg文件
function downloadFile(fileName, content){
    var encodedData = encodeURIComponent(content);

	/*var aLink = document.createElement('a');
	 var evt = document.createEvent("HTMLEvents");
	 evt.initEvent("click", false, false);//initEvent 不加后两个参数在FF下会报错
	 aLink.download = fileName;
	 aLink.href = 'data:application/bpmn20-xml;charset=UTF-8,' + encodedData;
	 aLink.dispatchEvent(evt);*/

    var a1new = document.createElement("a");
    //a1new.setAttribute("href", 'data:application/bpmn20-xml;charset=UTF-8,' + encodedData);
    a1new.setAttribute("href", 'data:application/xml;charset=UTF-8,' + encodedData);
    a1new.setAttribute("download", fileName);
    document.body.appendChild(a1new);
    a1new.click();
}

//选择指定组件
function selectIoElem(elemid) {
    var elem = getIORegistedElem(elemid);
    onCompSelected(elem);
    if (elemid.indexOf("SubProcess")>=0
        ||elemid.indexOf("Task")>=0
        ||elemid.indexOf("Gateway")>=0
        ||elemid.indexOf("Event")>=0) {
        fl_scrollToElem(elem);
    }
}

//取消选中css
function removeSelectCss() {
    var selected = Snap(".djs-container svg .selected");
    selected && selected.removeClass("selected");
    var ctxPad = $(".djs-container .djs-context-pad");
    ctxPad && ctxPad.removeClass("open");
    $(".djs-resizer").remove();
}

//取消选中css
function unSelect() {
    removeSelectCss();
    clearPropTabs();
    fl.curComp = null;
}

//组件被选中后的操作
function onCompSelected(elem, event) {
    console.debug("onCompSelected "+elem.id);

    var type1 = getBpmnType(elem);

	/*//避免重复点击
	 if (fl.curComp && fl.curComp.id==elem.id) {
	 //fix style issue
	 fl_loadDisplayStyles(elem, type1);
	 return;
	 }*/

    //取消选中css
    fl.curComp = null;
    //var selected = Snap(".djs-container svg .selected");
    //selected && selected.removeClass("selected");
    //清空属性栏
    clearPropTabs();
    //清空状态栏
    fl.stb.setText('');

    if (type1) {//为有效bpmn类型
        if (!fl.curflow.bpmnConfig[elem.id]) fl.curflow.bpmnConfig[elem.id] = {};
        //重复点击不修改fl.curComp
        if (!fl.curComp || fl.curComp.id!=elem.id) {
            fl.curComp = {};
            fl.curComp.id = elem.id;
            fl.curComp.name = elem.label;
            fl.curComp.type = type1;
            fl.curComp.width = elem.width;
            fl.curComp.height = elem.height;
        }
        //添加选中css,覆盖原有选中非选中以解决多选问题
        var s = Snap(".djs-container svg g[data-element-id='"+elem.id+"']");
        s && s.addClass("selected");
        //加载属性
        setPropTabs(elem);
        //on selected
        if (typeof(fldmeta_prop.funcOnCompSelected[type1])=='function') {
            fldmeta_prop.funcOnCompSelected[type1](elem.id, elem, event);
        }
    }
}

//组件被增加后的操作
function onCompAdded(event) {
    var elem = event.element;
    //console.debug("onCompAdded "+elem.id);
    !fl.isloading && onCompSelected(elem);

    //组件设置中的其他特殊处理
    var bpmnT = getBpmnType(elem);
    if ('function'==typeof(fldmeta_prop.funcOnCompAdded[bpmnT])) {
        fldmeta_prop.funcOnCompAdded[bpmnT](elem.id, elem);
    }
    //加载时修改样式
    if (fl.isloading && !MyUtilJs.isEmpty(fl.curflow.bpmnConfig[elem.id]) && fldmeta_prop.func[bpmnT] && 'function'==typeof(fldmeta_prop.func[bpmnT].onDisplayStyle)) {
        if ("custom"==fl.curflow.bpmnConfig[elem.id]["displayStyle"]) {
            for (var ds in fl.curflow.bpmnConfig[elem.id]) {
                if (ds.startsWith("displayStyle_")) {
                    var nv = fl.curflow.bpmnConfig[elem.id][ds];//实时值
                    var p = fldmeta_prop.control[bpmnT].styles.displayStyle["conditional-sub"].custom[ds];
                    var dv = typeof(p)=='function' ? p().value : p.value;
                    dv = typeof(dv)=='function' ? dv() : dv["default"];//默认值
                    if (nv!=dv) {//值与默认值不同时修改风格
                        fldmeta_prop.func[bpmnT].onDisplayStyle(elem.id, ds, nv);
                    }
                }
            }
        }
    }
}

//当组件被删除时调用
function onCompRemoved(event) {

    var elem = event.element;

    fl.curComp = null;
    if(fl.curflow.bpmnConfig) {
        delete fl.curflow.bpmnConfig[elem.id];
    }
    clearPropTabs();
    //清空状态栏
    fl.stb.setText('');

    //特殊处理
	/*if (elem.type=='bpmn:Participant') {
	 changeParticipantsLayout(elem);
	 } else if (elem.type=='bpmn:ParticipantGroup') {
	 changeParticipantGroupLayout(elem);
	 } else if (elem.type=='bpmn:MileStone') {
	 changeMileStonesLayout(elem);
	 } else if (elem.type=='bpmn:MileStoneGroup') {
	 changeMileStoneGroupsLayout(elem);
	 }*/

    //组件设置中的其他特殊处理
    var type1 = getBpmnType(elem);
    if (type1 && 'function'==typeof(fldmeta_prop.funcOnCompRemoved[type1])) {
        fldmeta_prop.funcOnCompRemoved[type1](elem.id, elem);
    }
}

//组件被改变时触发的方法
function onCompChanged(event) {
    var elem = event.element;
    var bpmnT = getBpmnType(event.element);
    console.debug('onCompChanged----------------type=='+bpmnT);

    if (!fl.isloading) {
        if (elem.type=='label') {
            var elemid = elem.id.substring(0, elem.id.length-6);
            onCompSelected(getIORegistedElem(elemid));
        } else if (bpmnT) {
            onCompSelected(elem);
        }
    }

    //组件设置中的其他特殊处理
    if (fl.curComp && 'function'==typeof(fldmeta_prop.funcOnCompChanged[bpmnT])) {
        fldmeta_prop.funcOnCompChanged[bpmnT](elem.id, elem);
    }
}

//close properties window
function clearPropTabs() {
    if (fmdc.grid_prop) fmdc.grid_prop.clearAll();
    //if (fl.propCell) fl.propCell.collapse();
}

//获取组件类型
function getBpmnType(elem) {
    var type = elem.type;
    if (type && type.indexOf("bpmn:")>=0) {
        type = type.substr(5);
        if (type=='SubProcess' && elem.collapsed===true || elem.isExpanded===false) {
            type = "SubProcessCollapsed";//链接子流程
        }
        if (fmd_i18n_flc_typenames[type]) {//仅返回支持的类型
            return type;
        }
    }
    return null;
}

//show properties window
function setPropTabs(elem) {
    fl.propCell.progressOn();
	/*setTimeout(function(){
	 fpf_showprop('control', fl.curComp.type);
	 fl.propCell.progressOff();
	 },
	 1
	 );*/
    fpf_showprop('control', fl.curComp.type);
    fl.propCell.progressOff();
}

//初始化svg辅助项
function initForCanvas() {
    var svg = Snap("svg");
    initFilters(svg);
    initMileStoneLane(svg);
}

//初始化滤镜
function initFilters(svg) {

    //效果滤镜
    for (var id in flfilters.effects) {
        var fe = svg.paper.filter(flfilters.effects[id]());
        fe.attr({"id":id});
    }

    //图片滤镜
    if (flfilters.images) {
        for (var id in flfilters.images) {
            var fi = svg.paper.filter('<image x="0" y="0" width="32" height="32" xlink:href="'+eval(flfilters.images[id].url)+'"></image>');
            fi.attr({"id":id, "width":32, "height":32});
        }
    }

    //渐变滤镜
    for (var id in flfilters.gradients) {
        var fg = svg.paper.gradient(flfilters.gradients[id]);
        fg.attr({"id":id});
    }

}

//初始化里程碑栏
function initMileStoneLane(svg) {
    fl.milestone = svg.paper.g();
    //corner rect //左上空正方形
    var rectc = svg.paper.rect(0, 0, fl.milestonestartx, fl.milestonelaneheight).attr(flstyle.elem.attrs.milestone);
    //milestone lane
    var rect = svg.paper.rect(0, 0, fl.curflow.otherConfig.canvaswidth + fl.milestonestartx, fl.milestonelaneheight).attr(flstyle.elem.attrs.milestonelane);
    var line = svg.paper.line(fl.milestonestartx, 0, fl.milestonestartx, fl.milestoneheight).attr(flstyle.elem.attrs.milestonelane_innerline);
    fl.milestone.add(rect, line, rectc).attr({"data-element-id":"milestonelane"});
    //文字
	/*var txt = fl.milestone.text(2, 16, "(0,0)").attr(flstyle.elem.attrs.milestonelane_timesplitlinetag);
	 fl.milestone.append(txt);*/

    Snap(".viewport").append(fl.milestone);

}

//当泳道大小发生改变时，修改所有泳道宽度，修改后序泳道的y轴位置
//elem 发生改变的泳道对象
function changeParticipantsLayout(elem) {

    //重置大小布局
    var pps = $("svg g[data-element-id^='Participant_']");
    //console.debug("============="+pps.length);
    var y = fl.milestonelaneheight;

    var width = fl.curflow.otherConfig.canvaswidth + fl.milestonestartx;
    if (elem && !fl.isloading) {//元素存在并且不是在加载时，则认为是在用户手工设计时
        width = Number(elem.width);//新宽度
        fl.curflow.otherConfig.canvaswidth = width - fl.milestonestartx;

        //对齐高度到指定数值倍数 10
		/*var unitHeight = 10;
		 var units = Math.round(elem.height/unitHeight);
		 if (units==0) (units = 1);	//最小为1
		 var newHeight = Math.round(unitHeight * units);
		 elem.height = newHeight;
		 console.debug("new height " + newHeight);*/
    }

    for (var i=0; i<pps.length; i++) {//循环处理每个泳道
        //console.debug("changeParticipantsLayout=====data-element-id="+Snap(pps[i]).attr("data-element-id"));
        var snapdom = Snap(pps[i]);//取得泳道对应的svg元素
        //修改辅助框的大小
        snapdom.select(".djs-hit").attr({"width":width});
        snapdom.select(".djs-visual > rect").attr({"width":width});
        snapdom.select(".djs-outline").attr({"width":width+12});
//			console.debug(snapdom);
//			snapdom.select(".djs-resizer-ne") && snapdom.select(".djs-resizer-ne").transform("matrix(0,1,-1,0,"+elem.width+",0)");
//			snapdom.select("g .djs-resizer-se") && snapdom.select("g .djs-resizer-se").transform("matrix(-1,0,0,-1,"+elem.width+","+elem.height+")");
//			snapdom.select("g .djs-resizer-sw") && snapdom.select("g .djs-resizer-sw").transform("matrix(0,-1,1,0,0,"+elem.height+")");
//			snapdom.transform("translate("+fl.lanegrouplabelwidth+","+y+")");//移动到新的y轴位置
        snapdom.transform("translate(0,"+y+")");//移动到新的y轴位置
        $(pps[i]).attr("x", 0);//修改x
        $(pps[i]).attr("y", y);//修改y
        //console.debug("=============="+snapdom.attr("x"));
        //console.debug("=============="+snapdom.attr("y"));
		/*if (elem && $("g[data-element-id='"+elem.id+"']").length && elem.id==Snap(pps[i]).attr("data-element-id")) {
		 elem.x = fl.lanegrouplabelwidth;
		 elem.y = y;
		 } else {*/
        var elem1 = getIORegistedElem(Snap(pps[i]).attr("data-element-id"));//在bpmn.io registry中的元素属性同时被修改
        elem1.x = 0;
        elem1.y = y;
        elem1.width = width;
        //修改ContextPad位置
        $(".djs-overlays-"+elem1.id).find(".djs-overlay-context-pad").css("left", width+9);
        //}
        y += Number(snapdom.select(".djs-visual > rect").attr("height"));//计算新的y值
        //console.debug("y=============="+y);
    }
    fl.canvasheight = y;

    //TODO 在当前修改了的泳道之后的泳道中的元素需要重新定位y值

    rerenderMileStoneLines();//重绘里程碑线
    //onTimelineNumberChanged();
}

//修改泳道分组排版
function changeParticipantGroupLayout(elem) {
    //判断是否为为第一个分组
    var els = getIORegistedElems("ParticipantGroup");
    //按y值排序
    els.sort(function(a,b) {
        return a.y - a.y;
    });
    //console.debug("============="+pps.length);
    var y = fl.milestonelaneheight;
    for (var i=0; i<els.length; i++) {
        var $dom = $("svg g[data-element-id^='"+els[i].id+"']");
        if ($dom.length==0) {
            continue;
        }
        var snapdom = Snap($dom[0]);//取得泳道对应的svg元素
        //修改辅助框的大小
        snapdom.select(".djs-hit").attr({"width":fl.lanegrouplabelwidth});
        snapdom.select(".djs-visual > rect").attr({"width":fl.lanegrouplabelwidth});
        snapdom.select(".djs-outline").attr({"width":fl.lanegrouplabelwidth+12});
        snapdom.transform("translate(0,"+y+")");//移动到新的y轴位置

        $dom.attr("x", 0);//修改x
        $dom.attr("y", y);//修改y

        //在bpmn.io registry中的元素属性同时被修改
        if (elem && $("svg g[data-element-id^='"+elem.id+"']").length && elem.id==els[i].id) {
            elem.x = 0;
            elem.y = y;

            //height
            elem.width = fl.lanegrouplabelwidth;

            //自动对其宽度到时间线并修改到期时间
            var newHeight = fl_adjustToUnit(elem.height);
            elem.height = newHeight;
            //console.debug("new height " + newHeight);
            snapdom.select(".djs-hit").attr({"height":newHeight});
            snapdom.select(".djs-visual > rect").attr({"height":newHeight});
            snapdom.select(".djs-outline").attr({"height":newHeight+12});

        } else {
            var elem1 = els[i];
            elem1.x = 0;
            elem1.y = y;
            elem1.width = fl.lanegrouplabelwidth;
        }
        y += Number(els[i].height);//记录新的y值
    }
}

//当里程碑大小发生改变时，修改所有里程碑宽度，修改后序里程碑的x轴位置
function changeMileStonesLayout(elem) {
    //判断是否为为第一个里程碑
//	console.debug("changeMileStonesLayout=====elem html "+elem);
//	console.debug("changeMileStonesLayout=====elem x y "+elem.x+"   "+elem.y);
//	console.debug("changeMileStonesLayout=====elem width height "+elem.width+"   "+elem.height);
//	console.debug("changeMileStonesLayout=====data-element-id="+elem.id);

    var pps = $("svg g[data-element-id^='MileStone_']");
    //console.debug("============="+pps.length);
    var x = fl.milestonestartx;
    for (var i=0; i<pps.length; i++) {
        //console.debug("changeMileStonesLayout=====data-element-id="+Snap(pps[i]).attr("data-element-id"));
        //获取svg dom元素
        var snapdom = Snap(pps[i]);
        //修改辅助框大小
        snapdom.select(".djs-hit").attr({"height":fl.milestoneheight});
        snapdom.select(".djs-visual > rect").attr({"height":fl.milestoneheight});
        snapdom.select(".djs-outline").attr({"height":fl.milestoneheight+12});
        snapdom.transform("translate("+x+","+fl.milestonestarty+")");//移动到新位置

        $(pps[i]).attr("x", x);
        $(pps[i]).attr("y", fl.milestonestarty);

        if (elem && $("svg g[data-element-id^='"+elem.id+"']").length && elem.id==Snap(pps[i]).attr("data-element-id")) {
            elem.x = x;
            elem.y = fl.milestonestarty;

            //height
            elem.height = fl.milestoneheight;

            //自动对其宽度到时间线并修改到期时间
            var unitWidth = 10;
            var units = Math.round(elem.width/unitWidth);
            if (units==0) (units = 1);	//最小为1
            var newWidth = Math.round(unitWidth * units);
            elem.width = newWidth;
            //console.debug("new width " + newWidth);

            snapdom.select(".djs-hit").attr({"width":newWidth});
            snapdom.select(".djs-visual > rect").attr({"width":newWidth});
            snapdom.select(".djs-outline").attr({"width":newWidth+12});

        } else {
            //在bpmn.io registry中的元素属性同时被修改
            var elem1 = getIORegistedElem(Snap(pps[i]).attr("data-element-id"));
            elem1.x = x;
            elem1.y = fl.milestonestarty;
            elem1.height = fl.milestoneheight;

        }
        x += Number(snapdom.select(".djs-visual > rect").attr("width"));//记录新x值
        //console.debug("x=============="+x);
    }
    //}
    rerenderMileStoneLines();//重绘里程碑线
    //如果宽度超限，重新计算泳道宽度
	/*if (x > fl.curflow.otherConfig.canvaswidth + fl.milestonestartx) {
	 fl.curflow.otherConfig.canvaswidth = x - fl.milestonestartx;
	 changeParticipantsLayout(elem);
	 }*/
}

//当里程碑分组大小发生改变时，修改所有里程碑分组x轴位置
function changeMileStoneGroupsLayout(elem) {
    //判断是否为为第一个里程碑分组
    var pps = $("svg g[data-element-id^='MileStoneGroup_']");
    //console.debug("============="+pps.length);
    var x = fl.milestonestartx;
    for (var i=0; i<pps.length; i++) {
        //console.debug("changeMileStonesLayout=====data-element-id="+Snap(pps[i]).attr("data-element-id"));
        //获取svg dom元素
        var snapdom = Snap(pps[i]);
        //修改辅助框大小
        snapdom.select(".djs-hit").attr({"height":fl.milestoneheight});
        snapdom.select(".djs-visual > rect").attr({"height":fl.milestoneheight});
        snapdom.select(".djs-outline").attr({"height":fl.milestoneheight+12});
        snapdom.transform("translate("+x+","+0+")");//移动到新位置

        $(pps[i]).attr("x", x);
        $(pps[i]).attr("y", 0);

        if (elem && $("svg g[data-element-id^='"+elem.id+"']").length && elem.id==Snap(pps[i]).attr("data-element-id")) {
            elem.x = x;
            elem.y = 0;

            //height
            elem.height = fl.milestoneheight;

            //自动对其宽度到时间线并修改到期时间
            var unitWidth = 10;
            var units = Math.round(elem.width/unitWidth);
            if (units==0) (units = 1);	//最小为1
            var newWidth = Math.round(unitWidth * units);
            elem.width = newWidth;
            //console.debug("new width " + newWidth);

            snapdom.select(".djs-hit").attr({"width":newWidth});
            snapdom.select(".djs-visual > rect").attr({"width":newWidth});
            snapdom.select(".djs-outline").attr({"width":newWidth+12});

        } else {
            //在bpmn.io registry中的元素属性同时被修改
            var elem1 = getIORegistedElem(Snap(pps[i]).attr("data-element-id"));
            elem1.x = x;
            elem1.y = 0;
            elem1.height = fl.milestoneheight;

        }
        x += Number(snapdom.select(".djs-visual > rect").attr("width"));
        //console.debug("x=============="+x);
    }
    //}
    rerenderMileStoneLines();
}

//重绘里程碑线
function rerenderMileStoneLines() {
    //先修改MileStone栏宽度
    Snap($("svg g[data-element-id='milestonelane'] > rect")[0]).attr({"width":fl.curflow.otherConfig.canvaswidth + fl.milestonestartx});
    //修改线
    var pps = $("svg g[data-element-id^='MileStone_']");
    //console.debug("rerenderMileStoneLines============="+pps.length);
    $(".milestone-splitline").remove();//先删除所有线
    if(pps.length) {
        var x = fl.milestonestartx;
        for (var i=0; i<pps.length; i++) {
            //console.debug("rerenderMileStoneLines=====data-element-id="+Snap(pps[i]).attr("data-element-id"));
            var snapdom = Snap(pps[i]);
            x += Number(snapdom.select(".djs-visual > rect").attr("width"));
            //console.debug("x=============="+x);
            var line = fl.milestone.line(x, fl.milestonestarty, x, fl.canvasheight).attr(flstyle.elem.attrs.milestone_splitline);
            fl.milestone.add(line);
        }
    }
}

//获取在bpmn.io registry中的元素
function getIORegistedElem(id) {
    var a = bi.iocanvas._elementRegistry.get(id);
    return a;
}

//获取在bpmn.io registry中的元素
function getIORegistedElems(type, attrs) {
    var a = [];
    for (var i in bi.iocanvas._elementRegistry._elements) {
        var el1 = bi.iocanvas._elementRegistry._elements[i].element;
        if (type==getBpmnType(el1)) {
            if (attrs && attrs.length) {
                var e1 = {};
                for (var j in attrs) {
                    e1[attrs[j]] = el1[attrs[j]];
                }
                a.push(e1);
            } else {
                a.push(el1);
            }
        }
    }
    return a;
}

//计算输入路线
//elemid - 元素id
//diagramJson - bpmn xml转换得到的json
function fl_getIncomingTransitions(elemid, diagramJson) {
    var seqflows = null;
    var set = {};
    if (diagramJson) {//如果diagramJson已存在
        var rs1 = JSONSelect.match('.@attributes:has(:root > .targetRef:val("'+elemid+'"))', diagramJson);
        //去掉重复项
        if (rs1 && rs1.length>0) {
            seqflows = [];
            for (var i in rs1) {
                if (!set[rs1[i].id]) {
                    seqflows.push(rs1[i]);
                    set[rs1[i].id] = true;
                }
            }
        }
    } else {
        bi.saveDiagram(function(err, xml){
            diagramJson = xml2json.fromStr(xml);
            var rs1 = JSONSelect.match('.@attributes:has(:root > .targetRef:val("'+elemid+'"))', diagramJson);
            //去掉重复项
            if (rs1 && rs1.length>0) {
                seqflows = [];
                for (var i in rs1) {
                    if (!set[rs1[i].id]) {
                        seqflows.push(rs1[i]);
                        set[rs1[i].id] = true;
                    }
                }
            }
        });
    }
    return seqflows;
}

//计算输出路线
function fl_getOutgoingTransitions(elemid, diagramJson) {
    var seqflows = null;
    var set = {};
    if (diagramJson) {
        var rs1 = JSONSelect.match('.@attributes:has(:root > .sourceRef:val("'+elemid+'"))', diagramJson);
        //去掉重复项
        if (rs1 && rs1.length>0) {
            seqflows = [];
            for (var i in rs1) {
                if (!set[rs1[i].id]) {
                    seqflows.push(rs1[i]);
                    set[rs1[i].id] = true;
                }
            }
        }
    } else {
        bi.saveDiagram(function(err, xml){
            diagramJson = xml2json.fromStr(xml);
            var rs1 = JSONSelect.match('.@attributes:has(:root > .sourceRef:val("'+elemid+'"))', diagramJson);
            //去掉重复项
            if (rs1 && rs1.length>0) {
                seqflows = [];
                for (var i in rs1) {
                    if (!set[rs1[i].id]) {
                        seqflows.push(rs1[i]);
                        set[rs1[i].id] = true;
                    }
                }
            }
        });
    }
    return seqflows;
}

//load modules 加载组件对应的js配置文件
function fpf_loadCompModules() {
    if ('dev'!=flvar.profile || !flvar.moduleRoot) {
        return;
    }
    fl_waitstart();
    try {
        fl_loadModules(resloc + flvar.moduleRoot+'/activity', flvar.activityModules);//任务环节
        fl_loadModules(resloc + flvar.moduleRoot+'/event', flvar.eventModules);//事件
        fl_loadModules(resloc + flvar.moduleRoot+'/gateway', flvar.gatewayModules);//网关
        fl_loadModules(resloc + flvar.moduleRoot+'/layout', flvar.layoutModules);//布局
        fl_loadModules(resloc + flvar.moduleRoot+'/transition', flvar.transitionModules);//连接线
        fl_loadModules(resloc + flvar.moduleRoot+'/misc', flvar.miscModules);//其他
    } catch(e){
        dhtmlx.message({expire:-1,type:"error",
            text:"An error has occured while loading modules.<br/> Please, see the log file!<br/>"+e
        });
    }
    fl_waitend();
}

function setByTenantCfg() {
    //allowAttach
    if (!flvar.tenantCfg.allowAttach && flvar.tenantCfg.clazz<=1) {
        if (fldmeta_prop.control.UserTask && fldmeta_prop.control.UserTask.properties) {
            delete fldmeta_prop.control.UserTask.properties.allowAttach;
        }
    }
}

//load modules
function fl_loadModules(modulepath, modules) {
    for (var i in modules) {
        loadModule(flvar.lang, modulepath, modules[i]);
    }
}

//change theme
function fl_selectTheme() {
    fl_checkChangeWithCallback(function() {
        var w1 = fm_createCenterWindow('wid_theme', fmd_i18n_b_sel + fmd_i18n_b_theme, 400, 400);

        var lo1 = w1.attachLayout("2E");
        lo1.cells('a').hideHeader();
        lo1.cells('b').hideHeader();
        lo1.cells('b').setHeight(50);

        var ragrid = lo1.cells('a').attachGrid();
        ragrid.setHeader([fmd_i18n_order, fmd_i18n_b_theme, fmd_i18n_b_theme, fmd_i18n_b_samplepreview],null,
            ["text-align:center;","text-align:center;","text-align:center;","text-align:center;"]);
        ragrid.setInitWidths("60,240,*,*");
        ragrid.setColTypes("ro,ro,ro,ro");
        ragrid.setColSorting("na,na,na,na");
        ragrid.setColAlign("center,left,center,center");
        ragrid.setEditable(false);
        ragrid.setColumnsVisibility("false,false,true,false");
        ragrid.setLang(flvar.lang);
        ragrid.init();

        ragrid.attachEvent("onRowDblClicked", function(rid) {
            w1.close();
            fl_changeTheme(rid);
        });

        var str2 = [{ type:"block" , name:"form_block_1", offsetLeft:(dhx_skin=="dhx_web"?"133":"143"), offsetTop:"0", list:[{ type:"button" , name:"fb_ok", value:fmd_i18n_b_ok, inputTop:0  }]}];
        var fm2 = lo1.cells('b').attachForm(str2);
        fm2.attachEvent("onButtonClick", function(btid) {
            if (btid=='fb_ok') {
                var rid = ragrid.getSelectedRowId();
                if (!rid) {
                    msg(fmd_i18n_msg_plssel_theme);
                    return;
                }
                w1.close();
                fl_changeTheme(rid);
            }
        });

        //load theme data
        if (fmdi18n.themes) {
            var gd = {rows:[]};
            var c = 1;
            for (var i in fmdi18n.themes) {
                gd.rows.push({"id":i, "data":[c++, fmdi18n.themes[i], i ,
                    '<img class="btn_pic" src="'+flvar.iconpath+'preview.gif" onclick="fl_showThemePreview(\''+i+'\')"></img>']});
            }
            ragrid.parse(gd, "json");
            //select current
            ragrid.selectRowById(fl.curflow.otherConfig.theme || "default");
        }
    });
}

//change theme
function fl_changeTheme(theme) {
    if (fl.curflow.otherConfig.theme!=theme) {
        editFlow(theme);
    }
}

//load theme preview png
function fl_showThemePreview(theme) {
    //alert(ctxpath+"/static/js/platform/designer/styles/sampleImages/"+theme+".png");
    $("body").append('<div class="previewbox"><img src="'+resloc+'/static/jsplatform/admin/designer/styles/sampleImages/'+theme+'.png"/></div>');
    $(".previewbox").click(function(){$(this).remove();});
}

//load theme file
function fl_loadThemeJs(callback) {
    !fl.curflow.otherConfig.theme && (fl.curflow.otherConfig.theme="default");
    $.getScript(resloc+"/static/jsplatform/admin/designer/styles/"+fl.curflow.otherConfig.theme+".js", callback);
}

//check save
function fl_checkClose() {
    if (fl.unSavedChange) {
        dhtmlx.message({
            "type": "confirm-warning",
            "title" : fmd_i18n_title_tip,
            "text": fmd_i18n_msg_unsavedchange,
            "ok" : fmd_i18n_b_yes,
            "cancel" : fmd_i18n_b_no,
            "callback" : function(y) {if (y) {closeWin();}}
        });
        return fmd_i18n_msg_unsavedchange;
    }
}

//check save
function fl_checkChangeWithCallback(callback){
    if (fl.unSavedChange) {
        dhtmlx.message({
            "type": "confirm-warning",
            "title" : fmd_i18n_title_tip,
            "text": fmd_i18n_msg_unsavedchange,
            "ok" : fmd_i18n_b_yes,
            "cancel" : fmd_i18n_b_no,
            "callback" : function(y) {if (y) {callback();}}
        });
    } else {
        callback();
    }
}

//bind scroll function to participants
//param elemid - participant element id
function fl_bindCanvasScroll(elemid) {

    var el = $("g[data-element-id='"+elemid+"']"), context;

    function handleMove(e) {
        var event = e.originalEvent;
        var start = context.start,
            position = fl_toPoint(event),
            delta = {"x" : position.x - start.x, "y" : position.y - start.y};

        if (!context.dragging && (Math.sqrt(Math.pow(delta.x, 2) + Math.pow(delta.y, 2)) > 15)) {
            context.dragging = true;

            // prevent mouse click in this"interaction sequence
            //ClickTrap.install();

            //el.css("cursor", "move");
        }

        if (context.dragging) {

            var lastPosition = context.last || context.start;
            delta = {"x" : position.x - lastPosition.x, "y" : position.y - lastPosition.y};

            bi.iocanvas.scroll({
                dx: delta.x,
                dy: delta.y
            });

            context.last = position;
        }

        fl_stopEvent(e);
    }

    function handleEnd(e) {
        var event = e.originalEvent;
        el.unbind("mousemove", handleMove);
        el.unbind("mouseup", handleEnd);
        //el.css("cursor", "default");
        context = null;
        fl_stopEvent(e);
    }

    function handleStart(e) {
        //console.log("handleStart");
        //console.log(e);
        var event = e.originalEvent;
        // reject non-left left mouse button or modifier key
        if (event.button || event.ctrlKey || event.shiftKey || event.altKey) {
            return;
        }

        context = { start: fl_toPoint(event) };

        el.on('mousemove', handleMove);
        el.on('mouseup', handleEnd);

        fl_stopEvent(e);
    }

    el.on('mousedown', handleStart);
    el.on('mouseout', function(){
        el.unbind("mousemove", handleMove);
        el.unbind("mouseup", handleEnd);
    });
}

//on scroll
function fl_resetScroll() {
    bi.iocanvas.zoom('fit-viewport');//定位到原点
    bi.iocanvas._setZoom(1);//重置缩放比并定位到原点
    fl_onScroll();
}

function fl_scrollToElem(elem) {
    fl_resetScroll();
    var svg = Snap('svg');
    var width = svg.node.width.baseVal.value, height=svg.node.height.baseVal.value,
        dx = elem.x - width/2 + elem.width/2,
        dy = elem.y - height/2 + elem.height/2;
    bi.iocanvas.scroll({dx:0-dx, dy:0-dy});
}

//on scroll
function fl_onScroll() {
    fl_refreshFloatHeader();//处理浮动栏
}

//初始化浮动的分组栏
function initFloatHeader() {
    if (!fl.floatheader) {
        var svg = Snap("svg");
        //float header
        fl.floatheader = svg.select("g[class='viewport']").g().attr({"id":"floatheader"});
        fl.floatheader.append(hbar);
        fl.floatheader.append(vbar);
        //步骤和阶段背景
        var hbar = fl.floatheader.g().attr({"id":"floatheader_hbar","style":"visibility:hidden"});
        var hbarrect = svg.paper.rect(0, 0, fl.curflow.otherConfig.canvaswidth + fl.milestonestartx, fl.milestonelaneheight).attr(flstyle.elem.attrs.milestonelane);
        hbar.append(hbarrect);
        //milestonegroup copies
        fl.floatheader.g().attr({"id":"floatheader_milestonegroup","style":"visibility:hidden"});
        //milestone copies
        fl.floatheader.g().attr({"id":"floatheader_milestone","style":"visibility:hidden"});
        //泳道和分组背景
        var vbar = fl.floatheader.g().attr({"id":"floatheader_vbar","style":"visibility:hidden"});
        var vbarrect = svg.paper.rect(0, 0, fl.milestonestartx, fl.canvasheight).attr(flstyle.elem.attrs.milestonelane);
        vbar.append(vbarrect);
        //participantgroup copies
        fl.floatheader.g().attr({"id":"floatheader_participantgroup","style":"visibility:hidden"});
        //lane copies
        fl.floatheader.g().attr({"id":"floatheader_lane","style":"visibility:hidden"});
    }
}

//float the header
function fl_refreshFloatHeader() {
    if (fl.isloading || !fl.floatheader) return;
    //console.log("scrolling");
    var deltaX, deltaY, trans = Snap("svg g[class='viewport']").attr("transform"),
        scale=1,
        hbar = fl.floatheader.select("g[id='floatheader_hbar']"),
        vbar = fl.floatheader.select("g[id='floatheader_vbar']");
    if (trans.global) {
        var tgs = trans.global.split(",");
        deltaX = parseInt(tgs[0].substr(1));
        if (tgs[1].indexOf("s")>=0) {
            deltaY = parseInt(tgs[1].split("s")[0]);
            scale = Number(tgs[1].split("s")[1].split(",")[0]);
        } else {
            deltaY = parseInt(tgs[1]);
        }
    }
    //console.log(trans.global+"  deltaX="+deltaX+"  deltaY="+deltaY+"  scale="+scale);

    //横向背景
    if (deltaY==undefined || deltaY>=0) {
        hbar.attr({"style":"visibility:hidden"});
    } else {
        hbar.attr({"style":"visibility:visible"});
        var xx = 0;
        var yy = 0 - deltaY/scale;
        hbar.transform("translate("+xx+","+yy+")");//移动到新位置
        hbar.attr("x", xx).attr("y", yy);
    }
    //纵向背景
    if (deltaX==undefined || deltaX>=0) {
        vbar.attr({"style":"visibility:hidden"});
    } else {
        vbar.attr({"style":"visibility:visible"});
        var xx = 0 - deltaX/scale;
        var yy = 0;
        vbar.transform("translate("+xx+","+yy+")");//移动到新位置
        vbar.attr("x", xx).attr("y", yy);
    }

    //处理步骤
    var mssg = fl.floatheader.select("g[id='floatheader_milestone']");
    if (mssg) {
        if (deltaY==undefined || deltaY>=0) {
            mssg.attr({"style":"visibility:hidden"});
        } else {
            mssg.attr({"style":"visibility:visible"});
            //move milestones
            var x1 = fl.milestonestartx;
            var y1 = fl.milestonestarty - deltaY/scale;
            var pps = $("svg g[id='floatheader_milestone'] g[data-element-id^='Copy_']");
            //console.debug("============="+pps.length);
            for (var i=0; i<pps.length; i++) {
                //console.debug("changeMileStonesLayout=====data-element-id="+Snap(pps[i]).attr("data-element-id"));
                //获取svg dom元素
                var snapdom = Snap(pps[i]);
                //修改辅助框大小
                //snapdom.select(".djs-visual > rect").attr({"height":fl.milestoneheight});
                snapdom.transform("translate("+x1+","+y1+")");//移动到新位置
                $(pps[i]).attr("x", x1);
                $(pps[i]).attr("y", y1);
                x1 += Number(snapdom.select(".djs-visual > rect").attr("width"));//记录新x值
            }
        }
    }

    //处理阶段
    var mgsg = fl.floatheader.select("g[id='floatheader_milestonegroup']");
    if (mgsg) {
        if (deltaY==undefined || deltaY>=0) {
            mgsg.attr({"style":"visibility:hidden"});
        } else {
            mgsg.attr({"style":"visibility:visible"});

            //move milestonegroups
            var x2 = fl.milestonestartx;
            var y2 = 0 - deltaY/scale;
            var pps = $("svg g[id='floatheader_milestonegroup'] g[data-element-id^='Copy_']");
            for (var i=0; i<pps.length; i++) {
                //获取svg dom元素
                var snapdom = Snap(pps[i]);
                //修改辅助框大小
                //snapdom.select(".djs-visual > rect").attr({"height":fl.milestoneheight});
                snapdom.transform("translate("+x2+","+y2+")");//移动到新位置
                $(pps[i]).attr("x", x2);
                $(pps[i]).attr("y", y2);
                x2 += Number(snapdom.select(".djs-visual > rect").attr("width"));//记录新x值
            }
        }
    }

    //处理业务分组
    var lgsg = fl.floatheader.select("g[id='floatheader_participantgroup']");
    if (lgsg) {
        if (deltaX==undefined || deltaX>=0) {
            lgsg.attr({"style":"visibility:hidden"});
        } else {
            lgsg.attr({"style":"visibility:visible"});

            //move milestonegroups
            var x3 = 0 - deltaX/scale;
            var y3 = fl.milestonelaneheight;
            var pps = $("svg g[id='floatheader_participantgroup'] g[data-element-id^='Copy_']");
            for (var i=0; i<pps.length; i++) {
                //获取svg dom元素
                var snapdom = Snap(pps[i]);
                //修改辅助框的大小
                //snapdom.select(".djs-visual > rect").attr({"width":fl.lanegrouplabelwidth});
                snapdom.transform("translate("+x3+","+y3+")");//移动到新位置
                $(pps[i]).attr("x", x3);
                $(pps[i]).attr("y", y3);
                y3 += Number(snapdom.select(".djs-visual > rect").attr("height"));//记录新y值
            }
        }
    }

    //处理泳道
    var lsg = fl.floatheader.select("g[id='floatheader_lane']");
    if (lsg) {
        if (deltaX==undefined || deltaX>=0) {
            lsg.attr({"style":"visibility:hidden"});
        } else {
            lsg.attr({"style":"visibility:visible"});

            //move milestonegroups
            var x4 = fl.lanegrouplabelwidth - deltaX/scale;
            var y4 = fl.milestonelaneheight;
            var pps = $("svg g[id='floatheader_lane'] g[data-element-id^='Copy_']");
            for (var i=0; i<pps.length; i++) {
                //获取svg dom元素
                var snapdom = Snap(pps[i]);
                //修改辅助框的大小
                snapdom.transform("translate("+x4+","+y4+")");//移动到新位置
                $(pps[i]).attr("x", x4);
                $(pps[i]).attr("y", y4);
                y4 += Number(snapdom.select(".djs-visual > rect").attr("height"));//记录新y值
            }
        }
    }
}

//prevent default event
fl___preventDefault = function(event) {
    return event && event.preventDefault();
}
//stop event propagation
fl___stopPropagation = function(event, immediate) {
    if (!event) {
        return;
    }
    if (event.stopPropagation) {
        event.stopPropagation();
    }
    if (immediate && event.stopImmediatePropagation) {
        event.stopImmediatePropagation();
    }
}
//prevent default event
fl_preventDefault = function(event) {
    fl___preventDefault(event);
    fl___preventDefault(fl_getOriginal(event));
}
//stop event propagation
fl_stopPropagation = function(event, immediate) {
    fl___stopPropagation(event, immediate);
    fl___stopPropagation(fl_getOriginal(event), immediate);
}
//get original event
fl_getOriginal = function(event) {
    return event.originalEvent || event.srcEvent;
}
//stop event propagation
fl_stopEvent = function(event, immediate) {
    fl_stopPropagation(event, immediate);
    fl_preventDefault(event);
}
//change mouse event to point json data
fl_toPoint = function(event) {

    if (event.pointers && event.pointers.length) {
        event = event.pointers[0];
    }

    if (event.touches && event.touches.length) {
        event = event.touches[0];
    }

    return event ? {
            x: event.clientX,
            y: event.clientY
        } : null;
}

//获取当前组件字体颜色
function flbpmn_getCompFontColor(elem) {
    if (elem) {
        if (elem.type=='label') {
            var elemid = elem.id.substring(0, elem.id.length-6);
            elem = getIORegistedElem(elemid);
        }
        if (fl.curflow.bpmnConfig[elem.id] && fl.curflow.bpmnConfig[elem.id]["displayStyle_fontColor"]) {
            return fl.curflow.bpmnConfig[elem.id]["displayStyle_fontColor"];
        } else {
            return flstyle.elem.fontColor[getBpmnType(elem)];
        }
    }
    return flstyle.elem.defaultFontColor;
}

//获取当前组件字体大小
function flbpmn_getCompFontSize(elem) {
    if (elem) {
        if (elem.type=='label') {
            var elemid = elem.id.substring(0, elem.id.length-6);
            elem = getIORegistedElem(elemid);
        }
        if (fl.curflow.bpmnConfig[elem.id] && fl.curflow.bpmnConfig[elem.id]["displayStyle_fontSize"]) {
            return fl.curflow.bpmnConfig[elem.id]["displayStyle_fontSize"];
        } else if (flstyle.elem.fontSize) {
            return flstyle.elem.fontSize[getBpmnType(elem)];
        }
    }
    return flstyle.elem.defaultFontSize || "16px";
}

//是否可以弹出组件菜单
function flbpmn_hasContextPad(elem) {
    return flvar.isEditor;
}

//是否可以移动 elems为数组
function flbpmn_canMove(elems) {
    if (!flvar.isEditor) return false;
    //必须为同一父对象
    var parentsids=[];
    if (elems && elems.length) {
        for (var i in elems) {
            if (elems[i].parent && parentsids.indexOf(elems[i].parent.id)<0) {
                parentsids.push(elems[i].parent.id);
            }
        }
        if (parentsids.length>1) {//父对象不唯一
            return false;
        }
    }
    return true;
}

//是否可以修改大小
function flbpmn_canResize(elem) {
    return flvar.isEditor;
}

//是否可以删除
function flbpmn_canDelete(elem) {
    return flvar.isEditor;
}

//是否可以修改类型
function flbpmn_canChangeType(elem) {
    return flvar.isEditor;
}

//是否可以修改属性
function flbpmn_canChangeProps(elem) {
    return flvar.isEditor;
}

//是否可以修改label
function flbpmn_canChangeLabel(elem) {
    return flvar.isEditor;
}

//是否可以修改线的中间点
function flbpmn_canConnUpdateWaypoints(elem) {
    return flvar.isEditor;
}

//是否可以修改线的某个片段位置
function flbpmn_canConnSegmentMove(elem) {
    return flvar.isEditor;
}

//当选择被改变时
function flbpmn_onSelectionChanged(event) {
    var selection = event.newSelection;
    //console.log("selection.length="+selection.length);
    //重置选择样式
    var selected = Snap(".djs-container svg").selectAll(".selected");
    selected.forEach(function(e){e.removeClass("selected")});
    for (var i in selection) {
        Snap(".djs-container svg g[data-element-id='"+selection[i].id+"']").addClass("selected");
    }

    //未选或多选清空属性面板
    if (selection.length != 1) {
        clearPropTabs();
    }
    //多选工具栏
    fl.mst = fl.mst || new MultiSelectionTool();
    if (selection.length > 1) {
        fl.mst.init(selection);
    } else {
        fl.mst.destroy();
    }
}

//当组件上编辑框结束时
function flbpmn_onDirectEditingComplete(text) {
    // 去掉特殊字符
    return text ? text.replace(/[^\u4e00-\u9fa5\w]/g, '') : text;
}

function MultiSelectionTool() {

    var mst = this;

    this.init = function(selection) {

        //过滤元素，只处理任务和链接子流程
        var operationTargets = ["UserTask", "SubProcessCollapsed"];
        selection = selection.filter(function(s){
            var bpmnT = getBpmnType(s);
            return bpmnT && operationTargets.indexOf(bpmnT)>=0;
        });


        if (!selection || selection.length<2) {
            return;//过滤后没有需要处理元素或只有一个
        } else if (!flbpmn_canMove(selection)) {
            return;//过滤后没有能够移动元素
        } else {
            for (var i in selection) {
                if (!flbpmn_canResize(selection[i])) return;//过滤后含有不能够修改大小元素
            }
        }
        //通过各种排序找出最大最小x,y,width,height
        selection.sort(function(a,b){return a.width-b.width});
        var minWidth = selection[0].width, maxWidth = selection[selection.length-1].width;
        selection.sort(function(a,b){return a.height-b.height});
        var minHeight = selection[0].height, maxHeight = selection[selection.length-1].height;
        selection.sort(function(a,b){return a.x-b.x});
        var minX = selection[0].x, maxX = selection[selection.length-1].x;
        selection.sort(function(a,b){return a.y-b.y});
        var minY = selection[0].y, maxY = selection[selection.length-1].y;

        //在最上面元素的上面弹出按钮栏
        var s1 = selection[0];
        var overlays = bi.renderer.get('overlays');
        overlays.add(s1, 'note', {
            position: {left: -10, top: -40},
            html: '<div class="djs-selection-toolpad">'+
            '<div class="btn_img st_align_left" title="'+fmd_i18n_b_st_align_left+'"><img src="'+flvar.iconpath_flowcanvas+'align-left.png"/></div>' +
            '<div class="btn_img st_align_right" title="'+fmd_i18n_b_st_align_right+'"><img src="'+flvar.iconpath_flowcanvas+'align-right.png"/></div>' +
            '<div class="btn_img st_align_top" title="'+fmd_i18n_b_st_align_top+'"><img src="'+flvar.iconpath_flowcanvas+'align-top.png"/></div>' +
            '<div class="btn_img st_align_bottom" title="'+fmd_i18n_b_st_align_bottom+'"><img src="'+flvar.iconpath_flowcanvas+'align-bottom.png"/></div>' +
            '<div class="btn_img st_resize_tominwidth" title="'+fmd_i18n_b_st_resize_tominwidth+'"><img src="'+flvar.iconpath_flowcanvas+'resize-tominwidth.png"/></div>' +
            '<div class="btn_img st_resize_tomaxwidth" title="'+fmd_i18n_b_st_resize_tomaxwidth+'"><img src="'+flvar.iconpath_flowcanvas+'resize-tomaxwidth.png"/></div>' +
            '<div class="btn_img st_resize_tominheight" title="'+fmd_i18n_b_st_resize_tominheight+'"><img src="'+flvar.iconpath_flowcanvas+'resize-tominheight.png"/></div>' +
            '<div class="btn_img st_resize_tomaxheight" title="'+fmd_i18n_b_st_resize_tomaxheight+'"><img src="'+flvar.iconpath_flowcanvas+'resize-tomaxheight.png"/></div>' +
            //'<div class="btn_img st_margin_h" title="'+fmd_i18n_b_st_margin_h+'"><img src="'+flvar.iconpath_flowcanvas+'margin-h.png"/></div>' +
            //'<div class="btn_img st_margin_v" title="'+fmd_i18n_b_st_margin_v+'"><img src="'+flvar.iconpath_flowcanvas+'margin-v.png"/></div>' +
            //'<div class="btn_img st_sync_theme" title="'+fmd_i18n_b_st_sync_theme+'"><img src="'+flvar.iconpath_flowcanvas+'sync-theme.png"/></div>' +
            '</div>'
        });
        var toolpad = $(".djs-overlay-container .djs-selection-toolpad");//按钮栏

        function align(axis) {
            fl.isloading = true, fl.ismoving = true;//修改loading标识未true，提高性能
            for (var i in selection) {
                var delta;
                if (axis=="left") {
                    delta = {x: minX - selection[i].x, y:0};//, maxX = minX;
                } else if (axis=="right") {
                    delta = {x: maxX - selection[i].x, y:0};//, minX = maxX;
                } else if (axis=="top") {
                    delta = {x: 0, y:minY - selection[i].y};//, maxY = minY;
                } else if (axis=="bottom") {
                    delta = {x: 0, y:maxY - selection[i].y};//, minY = maxY;
                }
                bi.modeling.moveShape(selection[i], delta);
                //子流程图片按钮位置更新
                var bpmnT = getBpmnType(selection[i]);
                if ('SubProcessCollapsed'==bpmnT) {
                    fldmeta_prop.funcOnCompChanged.SubProcessCollapsed(selection[i].id, selection[i]);
                }
            }
            fl.isloading = false, fl.ismoving = false;
        }

        function resizeto(boundto) {
            fl.isloading = true, fl.ismoving = true;//修改loading标识未true，提高性能
            for (var i in selection) {
                var newBounds = {x: selection[i].x, y:selection[i].y, width:selection[i].width, height:selection[i].height};
                if (boundto=="tominwidth") {
                    newBounds.width = minWidth;
                } else if (boundto=="tomaxwidth") {
                    newBounds.width = maxWidth;
                } else if (boundto=="tominheight") {
                    newBounds.height = minHeight;
                } else if (boundto=="tomaxheight") {
                    newBounds.height = maxHeight;
                }
                bi.modeling.resizeShape(selection[i], newBounds);
                //子流程图片按钮位置更新
                var bpmnT = getBpmnType(selection[i]);
                if ('SubProcessCollapsed'==bpmnT) {
                    fldmeta_prop.funcOnCompChanged.SubProcessCollapsed(selection[i].id, selection[i]);
                }
            }
            fl.isloading = false, fl.ismoving = false;
        }

        this.intervalh = 30;//每次调用init方法重置
        this.intervalv = 30;
        function margin(axis) {
            //先对齐后进行等间距排列
            if (axis=="h") {//横向horizontal
                align("top");
            } else if (axis=="v") {//纵向vertical
                align("left");
            }
            //按照链接排序，先排序链接在一起的，再排序无链接的
            var relationSeqs = [];	//是否环节之间有链接
            selection.sort(function(a,b){
                if (a.incoming && a.incoming.length) {
                    for (var s in a.incoming) {
                        if(a.incoming[s].source.id == b.id) {
                            relationSeqs.push(a.incoming[s]);
                            return 1;
                        }
                    }
                }
                if (a.outgoing && a.outgoing.length) {
                    for (var s in a.outgoing) {
                        if(a.outgoing[s].target.id == b.id) {
                            relationSeqs.push(a.outgoing[s]);
                            return -1;
                        }
                    }
                }
                return 0;
            });

            fl.isloading = true, fl.ismoving = true;//修改loading标识未true，提高性能
            var startx, starty;
            if (axis=="h") {//横向horizontal
                if (!relationSeqs.length) selection.sort(function(a,b){return a.x-b.x});//如果没有链接则按照x值排序
                startx = selection[0].x;
                mst.intervalh += 10;
            } else if (axis=="v") {//纵向vertical
                if (!relationSeqs.length) selection.sort(function(a,b){return a.y-b.y});//如果没有链接则按照y值排序
                starty = selection[0].y;
                mst.intervalv += 10;
            }
            for (var i in selection) {
                var delta;
                if (axis=="h") {
                    delta = {x: startx - selection[i].x, y:0};
                    startx += mst.intervalh + selection[i].width;
                } else if (axis=="v") {
                    delta = {x: 0, y: starty - selection[i].y};
                    starty += mst.intervalv + selection[i].height;
                }
                bi.modeling.moveShape(selection[i], delta);
                //子流程图片按钮位置更新
                var bpmnT = getBpmnType(selection[i]);
                if ('SubProcessCollapsed'==bpmnT) {
                    fldmeta_prop.funcOnCompChanged.SubProcessCollapsed(selection[i].id, selection[i]);
                }
            }
            //重新连线
            if (relationSeqs.length) {
                for (var i in relationSeqs) {
                    fl_recreateConnection(relationSeqs[i]);
                }
            }
            fl.isloading = false, fl.ismoving = false;
        }

        //统一风格
        function synctheme() {
            selection.sort(function(a,b){return a.x-b.x});
            var utStyles = null, spStyles = null;
            for (var i in selection) {
                var bpmnT = getBpmnType(selection[i]);
                if ('SubProcessCollapsed'==bpmnT) {
                    if (!spStyles) {
                        spStyles = {};
                        for (var c in fl.curflow.bpmnConfig[selection[i].id]) {
                            if (c.startsWith("displayStyle")) {
                                spStyles[c] = fl.curflow.bpmnConfig[selection[i].id][c];
                            }
                        }
                    } else {
                        for (var c in spStyles) {
                            fl.curflow.bpmnConfig[selection[i].id][c] = spStyles[c];
                        }
                        fl_loadDisplayStyles(selection[i], bpmnT);
                        onCompSelected(selection[i]);
                    }
                } else if ('UserTask'==bpmnT) {
                    if (!utStyles) {
                        utStyles = {};
                        for (var c in fl.curflow.bpmnConfig[selection[i].id]) {
                            if (c.startsWith("displayStyle")) {
                                utStyles[c] = fl.curflow.bpmnConfig[selection[i].id][c];
                            }
                        }
                    } else {
                        for (var c in utStyles) {
                            fl.curflow.bpmnConfig[selection[i].id][c] = utStyles[c];
                        }
                        fl_loadDisplayStyles(selection[i], bpmnT);
                        onCompSelected(selection[i]);
                    }
                }
            }
            clearPropTabs();
        }

        //左对齐
        toolpad.find(".btn_img.st_align_left").click(function(){align("left");});
        //右对齐
        toolpad.find(".btn_img.st_align_right").click(function(){align("right");});
        //上对齐
        toolpad.find(".btn_img.st_align_top").click(function(){align("top");});
        //下对齐
        toolpad.find(".btn_img.st_align_bottom").click(function(){align("bottom");});
        //最小宽
        toolpad.find(".btn_img.st_resize_tominwidth").click(function(){resizeto("tominwidth");});
        //最大宽
        toolpad.find(".btn_img.st_resize_tomaxwidth").click(function(){resizeto("tomaxwidth");});
        //最小高
        toolpad.find(".btn_img.st_resize_tominheight").click(function(){resizeto("tominheight");});
        //最大高
        toolpad.find(".btn_img.st_resize_tomaxheight").click(function(){resizeto("tomaxheight");});
        //横向等间距
        toolpad.find(".btn_img.st_margin_h").click(function(){margin("h");});
        //纵向等间距
        toolpad.find(".btn_img.st_margin_v").click(function(){margin("v");});
        //统一风格
        toolpad.find(".btn_img.st_sync_theme").click(function(){synctheme();});
    }
    this.destroy = function() {
        $(".djs-overlay-container .djs-selection-toolpad").parent().remove();
    }
}

//reload custom styles
function fl_reloadDisplayStylesForElems(elems) {
    if (elems && elems.length) {
        for (var i in elems) {
            fl_loadDisplayStyles(elems[i], getBpmnType(elems[i]));
        }
    }
}

//load custom styles
function fl_loadDisplayStyles(elem, bpmnT) {
    if (!MyUtilJs.isEmpty(fl.curflow.bpmnConfig[elem.id])
        && fldmeta_prop.func[bpmnT]
        && 'function'==typeof(fldmeta_prop.func[bpmnT].onDisplayStyle)
        && "custom"==fl.curflow.bpmnConfig[elem.id]["displayStyle"]) {
        for (var ds in fl.curflow.bpmnConfig[elem.id]) {
            if (ds.startsWith("displayStyle_")) {
                var nv = fl.curflow.bpmnConfig[elem.id][ds];//实时值
                var p = fldmeta_prop.control[bpmnT].styles.displayStyle["conditional-sub"].custom[ds];
                var dv = typeof(p)=='function' ? p().value : p.value;
                dv = typeof(dv)=='function' ? dv() : dv["default"];//默认值
                if (nv!=dv) {//值与默认值不同时修改风格
                    fldmeta_prop.func[bpmnT].onDisplayStyle(elem.id, ds, nv);
                }
            }
        }
    }
}

//对齐数字到最近的指定数字的倍数
function fl_adjustToUnit(n) {
    var unitWidth = 10;
    var units = Math.round(n/unitWidth);
    if (units==0) (units = 1);	//最小为1
    return Math.round(unitWidth * units);
}

//read error
function msgErrRead(xhr, textstate, errthr) {
    msgErr(fmd_i18n_msg_readfail + " [" + textstate + "]: " + errthr);
}

//commit error
function msgErrCommit(xhr, textstate, errthr) {
    msgErr(fmd_i18n_msg_commitfail + " [" + textstate + "]: " + errthr);
}

//show wait
function fl_waitstart(waitMsg) {
    return $(".waitMsg").html((waitMsg || "")+"&nbsp;&nbsp;"+fmd_i18n_msg_wait).promise().done(function(){$(".wait").show();});
}

//hide wait
function fl_waitend() {
    //console.log("fl_waitend called.");
    return $(".wait").hide().promise().done(function(){$(".waitMsg").html('')});
}

//show wait
function fl_waitmsg(waitMsg) {
    return $(".waitMsg").html(waitMsg || "").promise();
}

//down pdf
function fl_downloadpdf(){
    //组装参数
    var param = {};
    bi.saveSVG(function(err, xml){
        param.svg = xml;
    });
    param.name = fl.curflow.name;
    param.bpmnConfig = fl.curflow.bpmnConfig;

    var form = $("<form>");   //定义一个form表单
    form.attr('style', 'display:none');   //在form表单中添加查询参数
    form.attr('target', '');
    form.attr('method', 'post');
    form.attr('action', flvar.bpmeRest + "/"+flvar.flowRes+"/getProcesspdf");

    var input1 = $('<input>');
    input1.attr('type', 'hidden');
    input1.attr('name', 'param');
    input1.attr('value', JSON.stringify(param));
    $('body').append(form);  //将表单放置在web中
    form.append(input1);   //将查询参数控件提交到表单上
    form.submit();
}

//设置ajax行为
$.ajaxSetup({
    //超时
    timeout: 10000,
    //请求
    beforeSend: function(xhr) {
        //fl_waitstart();
    },
    //完成请求触发
    complete: function (req, txt) {
        //fl_waitend();
        if (req.readyState==4
            && req.getResponseHeader('Content-Type')
            && req.getResponseHeader('Content-Type').indexOf('html')!=-1
            && req.responseText
            && req.responseText.indexOf("META_DESC_LOGIN")!=-1) {
            dhtmlx.alert({
                text: fmd_i18n_msg_sessiontimeout,
                type: "alert-warning",
                callback: function() {
                    if (window.parent) {
                        window.parent.location.reload(true);
                    } else {
                        window.top.location.reload(true);
                    }
                }
            });
        }
    }
});



